@fraziym/axoniz 0.1.0

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 (512) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/dist/agents/definition.d.ts +15 -0
  4. package/dist/agents/definition.d.ts.map +1 -0
  5. package/dist/agents/definition.js +41 -0
  6. package/dist/agents/definition.js.map +1 -0
  7. package/dist/agents/index.d.ts +12 -0
  8. package/dist/agents/index.d.ts.map +1 -0
  9. package/dist/agents/index.js +11 -0
  10. package/dist/agents/index.js.map +1 -0
  11. package/dist/agents/registry.d.ts +17 -0
  12. package/dist/agents/registry.d.ts.map +1 -0
  13. package/dist/agents/registry.js +23 -0
  14. package/dist/agents/registry.js.map +1 -0
  15. package/dist/agents/specialized.d.ts +31 -0
  16. package/dist/agents/specialized.d.ts.map +1 -0
  17. package/dist/agents/specialized.js +75 -0
  18. package/dist/agents/specialized.js.map +1 -0
  19. package/dist/awareness/index.d.ts +14 -0
  20. package/dist/awareness/index.d.ts.map +1 -0
  21. package/dist/awareness/index.js +22 -0
  22. package/dist/awareness/index.js.map +1 -0
  23. package/dist/awareness/service.d.ts +180 -0
  24. package/dist/awareness/service.d.ts.map +1 -0
  25. package/dist/awareness/service.js +789 -0
  26. package/dist/awareness/service.js.map +1 -0
  27. package/dist/cli/entry.d.ts +13 -0
  28. package/dist/cli/entry.d.ts.map +1 -0
  29. package/dist/cli/entry.js +35 -0
  30. package/dist/cli/entry.js.map +1 -0
  31. package/dist/comms/telegram.d.ts +41 -0
  32. package/dist/comms/telegram.d.ts.map +1 -0
  33. package/dist/comms/telegram.js +332 -0
  34. package/dist/comms/telegram.js.map +1 -0
  35. package/dist/core/agent.d.ts +252 -0
  36. package/dist/core/agent.d.ts.map +1 -0
  37. package/dist/core/agent.js +1399 -0
  38. package/dist/core/agent.js.map +1 -0
  39. package/dist/core/auth.d.ts +23 -0
  40. package/dist/core/auth.d.ts.map +1 -0
  41. package/dist/core/auth.js +113 -0
  42. package/dist/core/auth.js.map +1 -0
  43. package/dist/core/authority.d.ts +118 -0
  44. package/dist/core/authority.d.ts.map +1 -0
  45. package/dist/core/authority.js +402 -0
  46. package/dist/core/authority.js.map +1 -0
  47. package/dist/core/backend/async_queue.d.ts +17 -0
  48. package/dist/core/backend/async_queue.d.ts.map +1 -0
  49. package/dist/core/backend/async_queue.js +66 -0
  50. package/dist/core/backend/async_queue.js.map +1 -0
  51. package/dist/core/backend/index.d.ts +24 -0
  52. package/dist/core/backend/index.d.ts.map +1 -0
  53. package/dist/core/backend/index.js +128 -0
  54. package/dist/core/backend/index.js.map +1 -0
  55. package/dist/core/backend/llama_server.d.ts +39 -0
  56. package/dist/core/backend/llama_server.d.ts.map +1 -0
  57. package/dist/core/backend/llama_server.js +182 -0
  58. package/dist/core/backend/llama_server.js.map +1 -0
  59. package/dist/core/backend/llamacpp.d.ts +40 -0
  60. package/dist/core/backend/llamacpp.d.ts.map +1 -0
  61. package/dist/core/backend/llamacpp.js +200 -0
  62. package/dist/core/backend/llamacpp.js.map +1 -0
  63. package/dist/core/backend/lmstudio.d.ts +53 -0
  64. package/dist/core/backend/lmstudio.d.ts.map +1 -0
  65. package/dist/core/backend/lmstudio.js +274 -0
  66. package/dist/core/backend/lmstudio.js.map +1 -0
  67. package/dist/core/backend/ollama.d.ts +25 -0
  68. package/dist/core/backend/ollama.d.ts.map +1 -0
  69. package/dist/core/backend/ollama.js +86 -0
  70. package/dist/core/backend/ollama.js.map +1 -0
  71. package/dist/core/backend/openai.d.ts +27 -0
  72. package/dist/core/backend/openai.d.ts.map +1 -0
  73. package/dist/core/backend/openai.js +84 -0
  74. package/dist/core/backend/openai.js.map +1 -0
  75. package/dist/core/backend/types.d.ts +73 -0
  76. package/dist/core/backend/types.d.ts.map +1 -0
  77. package/dist/core/backend/types.js +137 -0
  78. package/dist/core/backend/types.js.map +1 -0
  79. package/dist/core/chat_store.d.ts +77 -0
  80. package/dist/core/chat_store.d.ts.map +1 -0
  81. package/dist/core/chat_store.js +332 -0
  82. package/dist/core/chat_store.js.map +1 -0
  83. package/dist/core/cli.d.ts +82 -0
  84. package/dist/core/cli.d.ts.map +1 -0
  85. package/dist/core/cli.js +602 -0
  86. package/dist/core/cli.js.map +1 -0
  87. package/dist/core/config.d.ts +127 -0
  88. package/dist/core/config.d.ts.map +1 -0
  89. package/dist/core/config.js +295 -0
  90. package/dist/core/config.js.map +1 -0
  91. package/dist/core/debug.d.ts +8 -0
  92. package/dist/core/debug.d.ts.map +1 -0
  93. package/dist/core/debug.js +8 -0
  94. package/dist/core/debug.js.map +1 -0
  95. package/dist/core/downloader.d.ts +31 -0
  96. package/dist/core/downloader.d.ts.map +1 -0
  97. package/dist/core/downloader.js +180 -0
  98. package/dist/core/downloader.js.map +1 -0
  99. package/dist/core/errors.d.ts +80 -0
  100. package/dist/core/errors.d.ts.map +1 -0
  101. package/dist/core/errors.js +134 -0
  102. package/dist/core/errors.js.map +1 -0
  103. package/dist/core/extras.d.ts +160 -0
  104. package/dist/core/extras.d.ts.map +1 -0
  105. package/dist/core/extras.js +553 -0
  106. package/dist/core/extras.js.map +1 -0
  107. package/dist/core/health.d.ts +27 -0
  108. package/dist/core/health.d.ts.map +1 -0
  109. package/dist/core/health.js +96 -0
  110. package/dist/core/health.js.map +1 -0
  111. package/dist/core/history.d.ts +50 -0
  112. package/dist/core/history.d.ts.map +1 -0
  113. package/dist/core/history.js +276 -0
  114. package/dist/core/history.js.map +1 -0
  115. package/dist/core/index.d.ts +6 -0
  116. package/dist/core/index.d.ts.map +1 -0
  117. package/dist/core/index.js +6 -0
  118. package/dist/core/index.js.map +1 -0
  119. package/dist/core/intelligence/ast_index.d.ts +255 -0
  120. package/dist/core/intelligence/ast_index.d.ts.map +1 -0
  121. package/dist/core/intelligence/ast_index.js +1369 -0
  122. package/dist/core/intelligence/ast_index.js.map +1 -0
  123. package/dist/core/intelligence/confidence.d.ts +113 -0
  124. package/dist/core/intelligence/confidence.d.ts.map +1 -0
  125. package/dist/core/intelligence/confidence.js +298 -0
  126. package/dist/core/intelligence/confidence.js.map +1 -0
  127. package/dist/core/intelligence/context_compressor.d.ts +80 -0
  128. package/dist/core/intelligence/context_compressor.d.ts.map +1 -0
  129. package/dist/core/intelligence/context_compressor.js +245 -0
  130. package/dist/core/intelligence/context_compressor.js.map +1 -0
  131. package/dist/core/intelligence/daemon.d.ts +247 -0
  132. package/dist/core/intelligence/daemon.d.ts.map +1 -0
  133. package/dist/core/intelligence/daemon.js +842 -0
  134. package/dist/core/intelligence/daemon.js.map +1 -0
  135. package/dist/core/intelligence/index.d.ts +60 -0
  136. package/dist/core/intelligence/index.d.ts.map +1 -0
  137. package/dist/core/intelligence/index.js +101 -0
  138. package/dist/core/intelligence/index.js.map +1 -0
  139. package/dist/core/intelligence/predictor.d.ts +246 -0
  140. package/dist/core/intelligence/predictor.d.ts.map +1 -0
  141. package/dist/core/intelligence/predictor.js +590 -0
  142. package/dist/core/intelligence/predictor.js.map +1 -0
  143. package/dist/core/intelligence/reflex.d.ts +84 -0
  144. package/dist/core/intelligence/reflex.d.ts.map +1 -0
  145. package/dist/core/intelligence/reflex.js +344 -0
  146. package/dist/core/intelligence/reflex.js.map +1 -0
  147. package/dist/core/intelligence/self_correction.d.ts +121 -0
  148. package/dist/core/intelligence/self_correction.d.ts.map +1 -0
  149. package/dist/core/intelligence/self_correction.js +327 -0
  150. package/dist/core/intelligence/self_correction.js.map +1 -0
  151. package/dist/core/intelligence/skill_distiller.d.ts +100 -0
  152. package/dist/core/intelligence/skill_distiller.d.ts.map +1 -0
  153. package/dist/core/intelligence/skill_distiller.js +246 -0
  154. package/dist/core/intelligence/skill_distiller.js.map +1 -0
  155. package/dist/core/intelligence/swarm.d.ts +330 -0
  156. package/dist/core/intelligence/swarm.d.ts.map +1 -0
  157. package/dist/core/intelligence/swarm.js +1064 -0
  158. package/dist/core/intelligence/swarm.js.map +1 -0
  159. package/dist/core/intelligence/trajectory.d.ts +107 -0
  160. package/dist/core/intelligence/trajectory.d.ts.map +1 -0
  161. package/dist/core/intelligence/trajectory.js +330 -0
  162. package/dist/core/intelligence/trajectory.js.map +1 -0
  163. package/dist/core/logger.d.ts +111 -0
  164. package/dist/core/logger.d.ts.map +1 -0
  165. package/dist/core/logger.js +640 -0
  166. package/dist/core/logger.js.map +1 -0
  167. package/dist/core/loop.d.ts +123 -0
  168. package/dist/core/loop.d.ts.map +1 -0
  169. package/dist/core/loop.js +423 -0
  170. package/dist/core/loop.js.map +1 -0
  171. package/dist/core/memory.d.ts +81 -0
  172. package/dist/core/memory.d.ts.map +1 -0
  173. package/dist/core/memory.js +328 -0
  174. package/dist/core/memory.js.map +1 -0
  175. package/dist/core/metrics.d.ts +29 -0
  176. package/dist/core/metrics.d.ts.map +1 -0
  177. package/dist/core/metrics.js +109 -0
  178. package/dist/core/metrics.js.map +1 -0
  179. package/dist/core/models.d.ts +51 -0
  180. package/dist/core/models.d.ts.map +1 -0
  181. package/dist/core/models.js +146 -0
  182. package/dist/core/models.js.map +1 -0
  183. package/dist/core/optimizer.d.ts +34 -0
  184. package/dist/core/optimizer.d.ts.map +1 -0
  185. package/dist/core/optimizer.js +131 -0
  186. package/dist/core/optimizer.js.map +1 -0
  187. package/dist/core/persona.d.ts +35 -0
  188. package/dist/core/persona.d.ts.map +1 -0
  189. package/dist/core/persona.js +126 -0
  190. package/dist/core/persona.js.map +1 -0
  191. package/dist/core/rate_limit.d.ts +23 -0
  192. package/dist/core/rate_limit.d.ts.map +1 -0
  193. package/dist/core/rate_limit.js +62 -0
  194. package/dist/core/rate_limit.js.map +1 -0
  195. package/dist/core/runner.d.ts +63 -0
  196. package/dist/core/runner.d.ts.map +1 -0
  197. package/dist/core/runner.js +671 -0
  198. package/dist/core/runner.js.map +1 -0
  199. package/dist/core/stream_parser.d.ts +43 -0
  200. package/dist/core/stream_parser.d.ts.map +1 -0
  201. package/dist/core/stream_parser.js +140 -0
  202. package/dist/core/stream_parser.js.map +1 -0
  203. package/dist/core/tool_schemas.d.ts +26 -0
  204. package/dist/core/tool_schemas.d.ts.map +1 -0
  205. package/dist/core/tool_schemas.js +124 -0
  206. package/dist/core/tool_schemas.js.map +1 -0
  207. package/dist/core/which.d.ts +2 -0
  208. package/dist/core/which.d.ts.map +1 -0
  209. package/dist/core/which.js +35 -0
  210. package/dist/core/which.js.map +1 -0
  211. package/dist/goals/index.d.ts +9 -0
  212. package/dist/goals/index.d.ts.map +1 -0
  213. package/dist/goals/index.js +17 -0
  214. package/dist/goals/index.js.map +1 -0
  215. package/dist/goals/service.d.ts +115 -0
  216. package/dist/goals/service.d.ts.map +1 -0
  217. package/dist/goals/service.js +415 -0
  218. package/dist/goals/service.js.map +1 -0
  219. package/dist/goals/types.d.ts +47 -0
  220. package/dist/goals/types.d.ts.map +1 -0
  221. package/dist/goals/types.js +66 -0
  222. package/dist/goals/types.js.map +1 -0
  223. package/dist/index.d.ts +22 -0
  224. package/dist/index.d.ts.map +1 -0
  225. package/dist/index.js +22 -0
  226. package/dist/index.js.map +1 -0
  227. package/dist/integrations/autonomous_startup.d.ts +51 -0
  228. package/dist/integrations/autonomous_startup.d.ts.map +1 -0
  229. package/dist/integrations/autonomous_startup.js +419 -0
  230. package/dist/integrations/autonomous_startup.js.map +1 -0
  231. package/dist/integrations/goose.d.ts +105 -0
  232. package/dist/integrations/goose.d.ts.map +1 -0
  233. package/dist/integrations/goose.js +408 -0
  234. package/dist/integrations/goose.js.map +1 -0
  235. package/dist/integrations/index.d.ts +21 -0
  236. package/dist/integrations/index.d.ts.map +1 -0
  237. package/dist/integrations/index.js +17 -0
  238. package/dist/integrations/index.js.map +1 -0
  239. package/dist/integrations/mcp.d.ts +73 -0
  240. package/dist/integrations/mcp.d.ts.map +1 -0
  241. package/dist/integrations/mcp.js +307 -0
  242. package/dist/integrations/mcp.js.map +1 -0
  243. package/dist/integrations/mempalace.d.ts +18 -0
  244. package/dist/integrations/mempalace.d.ts.map +1 -0
  245. package/dist/integrations/mempalace.js +25 -0
  246. package/dist/integrations/mempalace.js.map +1 -0
  247. package/dist/integrations/multi_agent.d.ts +82 -0
  248. package/dist/integrations/multi_agent.d.ts.map +1 -0
  249. package/dist/integrations/multi_agent.js +280 -0
  250. package/dist/integrations/multi_agent.js.map +1 -0
  251. package/dist/integrations/unified_memory.d.ts +415 -0
  252. package/dist/integrations/unified_memory.d.ts.map +1 -0
  253. package/dist/integrations/unified_memory.js +1489 -0
  254. package/dist/integrations/unified_memory.js.map +1 -0
  255. package/dist/roles/beru.yaml +48 -0
  256. package/dist/sidecar/client.d.ts +63 -0
  257. package/dist/sidecar/client.d.ts.map +1 -0
  258. package/dist/sidecar/client.js +222 -0
  259. package/dist/sidecar/client.js.map +1 -0
  260. package/dist/sidecar/index.d.ts +7 -0
  261. package/dist/sidecar/index.d.ts.map +1 -0
  262. package/dist/sidecar/index.js +6 -0
  263. package/dist/sidecar/index.js.map +1 -0
  264. package/dist/startup.d.ts +22 -0
  265. package/dist/startup.d.ts.map +1 -0
  266. package/dist/startup.js +194 -0
  267. package/dist/startup.js.map +1 -0
  268. package/dist/tools/_internal.d.ts +90 -0
  269. package/dist/tools/_internal.d.ts.map +1 -0
  270. package/dist/tools/_internal.js +375 -0
  271. package/dist/tools/_internal.js.map +1 -0
  272. package/dist/tools/_smoke.d.ts +2 -0
  273. package/dist/tools/_smoke.d.ts.map +1 -0
  274. package/dist/tools/_smoke.js +203 -0
  275. package/dist/tools/_smoke.js.map +1 -0
  276. package/dist/tools/axodex_tools.d.ts +51 -0
  277. package/dist/tools/axodex_tools.d.ts.map +1 -0
  278. package/dist/tools/axodex_tools.js +181 -0
  279. package/dist/tools/axodex_tools.js.map +1 -0
  280. package/dist/tools/code_tools.d.ts +33 -0
  281. package/dist/tools/code_tools.d.ts.map +1 -0
  282. package/dist/tools/code_tools.js +191 -0
  283. package/dist/tools/code_tools.js.map +1 -0
  284. package/dist/tools/computer_tools.d.ts +59 -0
  285. package/dist/tools/computer_tools.d.ts.map +1 -0
  286. package/dist/tools/computer_tools.js +348 -0
  287. package/dist/tools/computer_tools.js.map +1 -0
  288. package/dist/tools/file_tools.d.ts +51 -0
  289. package/dist/tools/file_tools.d.ts.map +1 -0
  290. package/dist/tools/file_tools.js +424 -0
  291. package/dist/tools/file_tools.js.map +1 -0
  292. package/dist/tools/index.d.ts +13 -0
  293. package/dist/tools/index.d.ts.map +1 -0
  294. package/dist/tools/index.js +13 -0
  295. package/dist/tools/index.js.map +1 -0
  296. package/dist/tools/shell_tools.d.ts +39 -0
  297. package/dist/tools/shell_tools.d.ts.map +1 -0
  298. package/dist/tools/shell_tools.js +163 -0
  299. package/dist/tools/shell_tools.js.map +1 -0
  300. package/dist/tools/web_tools.d.ts +31 -0
  301. package/dist/tools/web_tools.d.ts.map +1 -0
  302. package/dist/tools/web_tools.js +117 -0
  303. package/dist/tools/web_tools.js.map +1 -0
  304. package/dist/voice/index.d.ts +41 -0
  305. package/dist/voice/index.d.ts.map +1 -0
  306. package/dist/voice/index.js +58 -0
  307. package/dist/voice/index.js.map +1 -0
  308. package/dist/voice/light_daemon.d.ts +197 -0
  309. package/dist/voice/light_daemon.d.ts.map +1 -0
  310. package/dist/voice/light_daemon.js +746 -0
  311. package/dist/voice/light_daemon.js.map +1 -0
  312. package/dist/voice/setup.d.ts +63 -0
  313. package/dist/voice/setup.d.ts.map +1 -0
  314. package/dist/voice/setup.js +443 -0
  315. package/dist/voice/setup.js.map +1 -0
  316. package/dist/voice/stt.d.ts +148 -0
  317. package/dist/voice/stt.d.ts.map +1 -0
  318. package/dist/voice/stt.js +361 -0
  319. package/dist/voice/stt.js.map +1 -0
  320. package/dist/voice/tts.d.ts +168 -0
  321. package/dist/voice/tts.d.ts.map +1 -0
  322. package/dist/voice/tts.js +698 -0
  323. package/dist/voice/tts.js.map +1 -0
  324. package/dist/voice/voice_loop.d.ts +101 -0
  325. package/dist/voice/voice_loop.d.ts.map +1 -0
  326. package/dist/voice/voice_loop.js +463 -0
  327. package/dist/voice/voice_loop.js.map +1 -0
  328. package/dist/voice/wake_word.d.ts +153 -0
  329. package/dist/voice/wake_word.d.ts.map +1 -0
  330. package/dist/voice/wake_word.js +458 -0
  331. package/dist/voice/wake_word.js.map +1 -0
  332. package/dist/voice/wav.d.ts +99 -0
  333. package/dist/voice/wav.d.ts.map +1 -0
  334. package/dist/voice/wav.js +306 -0
  335. package/dist/voice/wav.js.map +1 -0
  336. package/dist/web/broker.d.ts +33 -0
  337. package/dist/web/broker.d.ts.map +1 -0
  338. package/dist/web/broker.js +58 -0
  339. package/dist/web/broker.js.map +1 -0
  340. package/dist/web/open_browser.d.ts +2 -0
  341. package/dist/web/open_browser.d.ts.map +1 -0
  342. package/dist/web/open_browser.js +42 -0
  343. package/dist/web/open_browser.js.map +1 -0
  344. package/dist/web/server.d.ts +54 -0
  345. package/dist/web/server.d.ts.map +1 -0
  346. package/dist/web/server.js +831 -0
  347. package/dist/web/server.js.map +1 -0
  348. package/dist/web/static/app.js +4121 -0
  349. package/dist/web/static/index.html +2281 -0
  350. package/dist/web/static/js/MODULE_SUMMARY.md +124 -0
  351. package/dist/web/static/js/a11y.js +165 -0
  352. package/dist/web/static/js/admin.js +2145 -0
  353. package/dist/web/static/js/assistant.js +475 -0
  354. package/dist/web/static/js/calendar/reminders.js +114 -0
  355. package/dist/web/static/js/calendar/utils.js +166 -0
  356. package/dist/web/static/js/calendar.js +3348 -0
  357. package/dist/web/static/js/censor.js +356 -0
  358. package/dist/web/static/js/chat.js +4546 -0
  359. package/dist/web/static/js/chatRenderer.js +2294 -0
  360. package/dist/web/static/js/chatStream.js +276 -0
  361. package/dist/web/static/js/codeRunner.js +398 -0
  362. package/dist/web/static/js/color/hex.js +14 -0
  363. package/dist/web/static/js/colorPicker.js +453 -0
  364. package/dist/web/static/js/compare/icons.js +77 -0
  365. package/dist/web/static/js/compare/index.js +1482 -0
  366. package/dist/web/static/js/compare/models.js +104 -0
  367. package/dist/web/static/js/compare/panes.js +817 -0
  368. package/dist/web/static/js/compare/probe.js +78 -0
  369. package/dist/web/static/js/compare/scoreboard.js +223 -0
  370. package/dist/web/static/js/compare/selector.js +1335 -0
  371. package/dist/web/static/js/compare/state.js +58 -0
  372. package/dist/web/static/js/compare/stream.js +695 -0
  373. package/dist/web/static/js/compare/vote.js +254 -0
  374. package/dist/web/static/js/cookbook-diagnosis.js +694 -0
  375. package/dist/web/static/js/cookbook-hwfit.js +1888 -0
  376. package/dist/web/static/js/cookbook.js +2093 -0
  377. package/dist/web/static/js/cookbookDownload.js +633 -0
  378. package/dist/web/static/js/cookbookProgressSignal.js +29 -0
  379. package/dist/web/static/js/cookbookRunning.js +3506 -0
  380. package/dist/web/static/js/cookbookServe.js +2128 -0
  381. package/dist/web/static/js/document.js +9731 -0
  382. package/dist/web/static/js/documentLibrary.js +3376 -0
  383. package/dist/web/static/js/dragSort.js +265 -0
  384. package/dist/web/static/js/editor/ai-inpaint.js +374 -0
  385. package/dist/web/static/js/editor/ai-models.js +273 -0
  386. package/dist/web/static/js/editor/ai-rembg.js +230 -0
  387. package/dist/web/static/js/editor/ai-tool-runner.js +147 -0
  388. package/dist/web/static/js/editor/ai-tools-misc.js +216 -0
  389. package/dist/web/static/js/editor/build/controls.js +366 -0
  390. package/dist/web/static/js/editor/build/popups.js +112 -0
  391. package/dist/web/static/js/editor/build/right-panel.js +200 -0
  392. package/dist/web/static/js/editor/build/toolbar.js +73 -0
  393. package/dist/web/static/js/editor/build/topbar.js +131 -0
  394. package/dist/web/static/js/editor/build/transform-popup.js +109 -0
  395. package/dist/web/static/js/editor/canvas-coords.js +21 -0
  396. package/dist/web/static/js/editor/canvas-events.js +197 -0
  397. package/dist/web/static/js/editor/canvas-transforms.js +132 -0
  398. package/dist/web/static/js/editor/checkerboard.js +24 -0
  399. package/dist/web/static/js/editor/clipboard-and-drop.js +133 -0
  400. package/dist/web/static/js/editor/composite-helpers.js +83 -0
  401. package/dist/web/static/js/editor/filters/blur.js +118 -0
  402. package/dist/web/static/js/editor/filters/edge-feather.js +70 -0
  403. package/dist/web/static/js/editor/fx/adj-popup.js +677 -0
  404. package/dist/web/static/js/editor/fx/filter-string.js +38 -0
  405. package/dist/web/static/js/editor/fx/histogram.js +67 -0
  406. package/dist/web/static/js/editor/fx/pixel-pass.js +249 -0
  407. package/dist/web/static/js/editor/harmonize-masks.js +116 -0
  408. package/dist/web/static/js/editor/history-panel.js +176 -0
  409. package/dist/web/static/js/editor/keyboard-shortcuts.js +271 -0
  410. package/dist/web/static/js/editor/layer-helpers.js +131 -0
  411. package/dist/web/static/js/editor/layer-panel.js +600 -0
  412. package/dist/web/static/js/editor/mask-utils.js +91 -0
  413. package/dist/web/static/js/editor/shortcuts-popover.js +149 -0
  414. package/dist/web/static/js/editor/slider-ux.js +191 -0
  415. package/dist/web/static/js/editor/snap.js +107 -0
  416. package/dist/web/static/js/editor/state.js +257 -0
  417. package/dist/web/static/js/editor/stroke-pipeline.js +162 -0
  418. package/dist/web/static/js/editor/stroke-tool-sliders.js +64 -0
  419. package/dist/web/static/js/editor/tools/clone.js +81 -0
  420. package/dist/web/static/js/editor/tools/crop.js +137 -0
  421. package/dist/web/static/js/editor/tools/flood-fill.js +72 -0
  422. package/dist/web/static/js/editor/tools/lasso-mask.js +171 -0
  423. package/dist/web/static/js/editor/tools/lasso.js +65 -0
  424. package/dist/web/static/js/editor/tools/move.js +79 -0
  425. package/dist/web/static/js/editor/tools/stroke.js +123 -0
  426. package/dist/web/static/js/editor/tools/transform-drag.js +174 -0
  427. package/dist/web/static/js/editor/tools/transform-handles.js +271 -0
  428. package/dist/web/static/js/editor/tools/transform-session.js +381 -0
  429. package/dist/web/static/js/editor/tools/wand.js +46 -0
  430. package/dist/web/static/js/editor/wire-import.js +148 -0
  431. package/dist/web/static/js/editor/wire-inpaint-controls.js +164 -0
  432. package/dist/web/static/js/editor/wire-merge-buttons.js +103 -0
  433. package/dist/web/static/js/editor/wire-selection-controls.js +170 -0
  434. package/dist/web/static/js/editor/wire-topbar-menus.js +174 -0
  435. package/dist/web/static/js/editor/wire-topbar-overflow.js +49 -0
  436. package/dist/web/static/js/editor/wire-topbar.js +188 -0
  437. package/dist/web/static/js/emailInbox.js +1245 -0
  438. package/dist/web/static/js/emailLibrary/replyRecipients.js +27 -0
  439. package/dist/web/static/js/emailLibrary/signatureFold.js +339 -0
  440. package/dist/web/static/js/emailLibrary/state.js +34 -0
  441. package/dist/web/static/js/emailLibrary/utils.js +202 -0
  442. package/dist/web/static/js/emailLibrary.js +5217 -0
  443. package/dist/web/static/js/emojiPicker.js +311 -0
  444. package/dist/web/static/js/escMenuStack.js +102 -0
  445. package/dist/web/static/js/fileHandler.js +295 -0
  446. package/dist/web/static/js/gallery.js +2835 -0
  447. package/dist/web/static/js/galleryEditor.js +3798 -0
  448. package/dist/web/static/js/group.js +955 -0
  449. package/dist/web/static/js/init.js +405 -0
  450. package/dist/web/static/js/keyboard-shortcuts.js +292 -0
  451. package/dist/web/static/js/langIcons.js +187 -0
  452. package/dist/web/static/js/markdown/tableRow.js +19 -0
  453. package/dist/web/static/js/markdown.js +806 -0
  454. package/dist/web/static/js/memory.js +1418 -0
  455. package/dist/web/static/js/modalManager.js +1552 -0
  456. package/dist/web/static/js/modalSnap.js +782 -0
  457. package/dist/web/static/js/model/matchKey.js +19 -0
  458. package/dist/web/static/js/modelPicker.js +695 -0
  459. package/dist/web/static/js/modelSort.js +33 -0
  460. package/dist/web/static/js/models.js +642 -0
  461. package/dist/web/static/js/notes.js +5054 -0
  462. package/dist/web/static/js/package.json +1 -0
  463. package/dist/web/static/js/platform.js +47 -0
  464. package/dist/web/static/js/presets.js +1110 -0
  465. package/dist/web/static/js/providers.js +93 -0
  466. package/dist/web/static/js/rag.js +177 -0
  467. package/dist/web/static/js/research/jobs.js +335 -0
  468. package/dist/web/static/js/research/panel.js +1233 -0
  469. package/dist/web/static/js/researchSynapse.js +225 -0
  470. package/dist/web/static/js/search-chat.js +201 -0
  471. package/dist/web/static/js/search.js +52 -0
  472. package/dist/web/static/js/section-management.js +260 -0
  473. package/dist/web/static/js/sessions.js +3108 -0
  474. package/dist/web/static/js/settings.js +4991 -0
  475. package/dist/web/static/js/sidebar-layout.js +573 -0
  476. package/dist/web/static/js/signature.js +504 -0
  477. package/dist/web/static/js/skills.js +1908 -0
  478. package/dist/web/static/js/slashAutocomplete.js +267 -0
  479. package/dist/web/static/js/slashCommands.js +6099 -0
  480. package/dist/web/static/js/spinner.js +406 -0
  481. package/dist/web/static/js/storage.js +123 -0
  482. package/dist/web/static/js/tasks.js +2699 -0
  483. package/dist/web/static/js/theme.js +2086 -0
  484. package/dist/web/static/js/tileManager.js +386 -0
  485. package/dist/web/static/js/tourAutoplay.js +133 -0
  486. package/dist/web/static/js/tourHints.js +179 -0
  487. package/dist/web/static/js/tts-ai.js +521 -0
  488. package/dist/web/static/js/ui.js +1288 -0
  489. package/dist/web/static/js/util/ordinal.js +13 -0
  490. package/dist/web/static/js/voiceRecorder.js +283 -0
  491. package/dist/web/static/js/windowDrag.js +326 -0
  492. package/dist/web/static/js/windowResize.js +233 -0
  493. package/dist/web/static/landing.html +824 -0
  494. package/dist/web/static/lib/docx.umd.min.js +20116 -0
  495. package/dist/web/static/lib/highlight.min.js +1213 -0
  496. package/dist/web/static/lib/html2pdf.bundle.min.js +3 -0
  497. package/dist/web/static/lib/mammoth.browser.min.js +21 -0
  498. package/dist/web/static/lib/qrcode.min.js +8 -0
  499. package/dist/web/static/lib/xlsx.full.min.js +24 -0
  500. package/dist/web/static/login.html +582 -0
  501. package/dist/web/static/manifest.json +15 -0
  502. package/dist/web/static/style.css +35733 -0
  503. package/dist/web/static/sw.js +144 -0
  504. package/dist/workflows/engine.d.ts +162 -0
  505. package/dist/workflows/engine.d.ts.map +1 -0
  506. package/dist/workflows/engine.js +625 -0
  507. package/dist/workflows/engine.js.map +1 -0
  508. package/dist/workflows/index.d.ts +8 -0
  509. package/dist/workflows/index.d.ts.map +1 -0
  510. package/dist/workflows/index.js +14 -0
  511. package/dist/workflows/index.js.map +1 -0
  512. package/package.json +112 -0
@@ -0,0 +1,817 @@
1
+ // compare/panes.js — pane lifecycle, actions, layout
2
+ import state from './state.js';
3
+ import { _persistSelections } from './models.js';
4
+ import { buildVoteBar } from './vote.js';
5
+ import {
6
+ ICON_REROLL, ICON_COPY, ICON_EXPAND, ICON_COLLAPSE, ICON_CLOSE,
7
+ ICON_PLAY, ICON_CODE, SEND_SVG,
8
+ } from './icons.js';
9
+ import { _clearProbeWaves } from './probe.js';
10
+ import Storage from '../storage.js';
11
+ import uiModule from '../ui.js';
12
+ import spinnerModule from '../spinner.js';
13
+ import { bindMenuDismiss } from '../escMenuStack.js';
14
+
15
+ var escapeHtml = uiModule.esc;
16
+
17
+ // ── Lazy-registered functions from compare.js (avoids circular imports) ──
18
+ let _setSendBtn = null;
19
+ let _deactivate = null;
20
+ let _streamToPane = null;
21
+ let _renderSearchResults = null;
22
+ let _fetchModels = null;
23
+
24
+ /** Register external functions that live in compare.js or sibling modules. */
25
+ function registerPaneActions({ setSendBtn, deactivate, streamToPane, renderSearchResults, fetchModels }) {
26
+ if (setSendBtn) _setSendBtn = setSendBtn;
27
+ if (deactivate) _deactivate = deactivate;
28
+ if (streamToPane) _streamToPane = streamToPane;
29
+ if (renderSearchResults) _renderSearchResults = renderSearchResults;
30
+ if (fetchModels) _fetchModels = fetchModels;
31
+ }
32
+
33
+ /** Slot label: A/B/C in parallel mode, 1/2/3 in sequential. */
34
+ function _slotChar(i) { return state._parallel ? String.fromCharCode(65 + i) : String(i + 1); }
35
+
36
+ // ── Stop / reroll ──
37
+
38
+ function stopAll() {
39
+ state._abortControllers.forEach(ac => { if (ac) ac.abort(); });
40
+ state._abortControllers = [];
41
+ state._streaming = false;
42
+ if (_setSendBtn) _setSendBtn('send');
43
+ // Re-enable header buttons
44
+ document.querySelectorAll('#compare-shuffle-btn, #compare-check-btn, #compare-add-btn').forEach(b => {
45
+ b.disabled = false; b.style.opacity = '0.7'; b.style.pointerEvents = '';
46
+ });
47
+ }
48
+
49
+ function stopPane(paneIdx) {
50
+ const ac = state._abortControllers[paneIdx];
51
+ if (ac) {
52
+ ac.abort();
53
+ state._abortControllers[paneIdx] = null;
54
+ }
55
+ // Hide stop button, show reroll
56
+ const pane = document.querySelector(`.compare-pane[data-pane="${paneIdx}"]`);
57
+ if (pane) {
58
+ const stopBtn = pane.querySelector('.pane-stop-btn');
59
+ if (stopBtn) stopBtn.style.display = 'none';
60
+ pane.querySelectorAll('.pane-needs-response').forEach(b => b.style.display = '');
61
+ }
62
+ // Remove spinner if present
63
+ const hist = document.getElementById('cmp-history-' + paneIdx);
64
+ if (hist) {
65
+ const lastAi = hist.querySelector('.msg-ai:last-child');
66
+ if (lastAi && lastAi._spinner) { lastAi._spinner.destroy(); lastAi._spinner = null; }
67
+ const body = lastAi && lastAi.querySelector('.body');
68
+ if (body && !body.textContent.trim()) {
69
+ body.innerHTML = '<span style="opacity:0.4;font-style:italic;">Stopped</span>';
70
+ }
71
+ }
72
+ }
73
+
74
+ async function rerollPane(paneIdx, overrideTimeout) {
75
+ // Allow reroll even while other panes stream — just stop this pane first
76
+ if (state._abortControllers[paneIdx]) stopPane(paneIdx);
77
+ const hist = document.getElementById('cmp-history-' + paneIdx);
78
+ // Reset preview state
79
+ const _ri = document.getElementById('cmp-iframe-' + paneIdx);
80
+ if (_ri) { _ri.srcdoc = ''; _ri.style.display = 'none'; _ri._htmlCode = null; }
81
+ const _rp = document.getElementById('cmp-preview-' + paneIdx);
82
+ if (_rp) { _rp.style.display = 'none'; _rp.classList.remove('active'); }
83
+ if (hist) hist.style.display = '';
84
+ if (!hist) return;
85
+ const userBodies = hist.querySelectorAll('.msg-user .body');
86
+ const firstUserText = userBodies.length > 0 ? userBodies[0].textContent : '';
87
+ if (!firstUserText) return;
88
+
89
+ // Clear all messages and start fresh
90
+ hist.innerHTML = '';
91
+ const userMsg = document.createElement('div');
92
+ userMsg.className = 'msg msg-user';
93
+ userMsg.innerHTML = '<div class="role">You</div><div class="body">' + escapeHtml(firstUserText) + '</div>';
94
+ hist.appendChild(userMsg);
95
+
96
+ // Reset badge and timer
97
+ const badge = document.getElementById('cmp-badge-' + paneIdx);
98
+ if (badge) { badge.textContent = ''; badge.style.color = ''; }
99
+ const timer = document.getElementById('cmp-timer-' + paneIdx);
100
+ if (timer) timer.textContent = '';
101
+
102
+ // Search mode: re-query the search provider
103
+ if (state._compareMode === 'search') {
104
+ const aiMsg = document.createElement('div');
105
+ aiMsg.className = 'msg msg-ai';
106
+ aiMsg.innerHTML = '<div class="role">Search</div><div class="body"></div>';
107
+ const aiBody = aiMsg.querySelector('.body');
108
+ if (spinnerModule) {
109
+ const spinner = spinnerModule.create('Searching...', 'right');
110
+ aiBody.appendChild(spinner.createElement());
111
+ spinner.start();
112
+ }
113
+ hist.appendChild(aiMsg);
114
+ hist.scrollTop = hist.scrollHeight;
115
+
116
+ const m = state._selectedModels[paneIdx];
117
+ const fd = new FormData();
118
+ fd.append('query', firstUserText);
119
+ fd.append('provider', m.model);
120
+ fd.append('count', '10');
121
+ try {
122
+ const ac = new AbortController();
123
+ state._abortControllers[paneIdx] = ac;
124
+ const t0 = performance.now();
125
+ const res = await fetch(`${state.API_BASE}/api/search/query`, { method: 'POST', body: fd, signal: ac.signal });
126
+ const data = await res.json();
127
+ const elapsed = ((performance.now() - t0) / 1000).toFixed(2);
128
+ aiBody.innerHTML = '';
129
+ if (data.error) {
130
+ aiBody.innerHTML = '<div style="color:var(--color-error);font-size:0.85em;">Error: ' + escapeHtml(data.error) + '</div>';
131
+ } else if (!data.results || data.results.length === 0) {
132
+ aiBody.innerHTML = '<div style="color:color-mix(in srgb, var(--fg) 50%, transparent);font-size:0.85em;font-style:italic;">No results found</div>';
133
+ } else {
134
+ aiBody.appendChild(_renderSearchResults(data));
135
+ }
136
+ const footer = document.createElement('div');
137
+ footer.className = 'msg-footer';
138
+ const span = document.createElement('span');
139
+ span.className = 'response-metrics';
140
+ const parts = [];
141
+ if (data.results) parts.push(data.results.length + ' results');
142
+ parts.push(elapsed + 's');
143
+ span.textContent = parts.join(' | ');
144
+ footer.appendChild(span);
145
+ aiMsg.appendChild(footer);
146
+ } catch (err) {
147
+ aiBody.innerHTML = '<div style="color:var(--color-error);font-size:0.85em;">Error: ' + escapeHtml(err.message) + '</div>';
148
+ }
149
+ state._abortControllers[paneIdx] = null;
150
+ hist.scrollTop = hist.scrollHeight;
151
+ return;
152
+ }
153
+
154
+ // Chat/agent mode: stream via session
155
+ const aiMsg = document.createElement('div');
156
+ aiMsg.className = 'msg msg-ai';
157
+ aiMsg.innerHTML = '<div class="role">AI</div><div class="body"></div>';
158
+ const aiBody = aiMsg.querySelector('.body');
159
+ if (spinnerModule) {
160
+ const label = overrideTimeout ? 'Retrying (' + overrideTimeout + 's)...' : 'Re-rolling...';
161
+ const spinner = spinnerModule.create(label, 'right');
162
+ aiBody.appendChild(spinner.createElement());
163
+ spinner.start();
164
+ aiMsg._spinner = spinner;
165
+ }
166
+ hist.appendChild(aiMsg);
167
+ hist.scrollTop = hist.scrollHeight;
168
+
169
+ const opts = { skipBadge: true };
170
+ if (overrideTimeout) opts.timeout = overrideTimeout;
171
+ await _streamToPane(paneIdx, state._paneSessionIds[paneIdx], firstUserText, aiMsg, opts);
172
+ }
173
+
174
+ // ── Expand / preview / copy ──
175
+
176
+ function toggleExpandPane(paneIdx, btn) {
177
+ const grid = document.querySelector('.compare-grid');
178
+ if (!grid) return;
179
+ const panes = grid.querySelectorAll('.compare-pane');
180
+ const target = panes[paneIdx];
181
+ if (!target) return;
182
+
183
+ if (target.classList.contains('expanded')) {
184
+ target.classList.remove('expanded');
185
+ panes.forEach(p => { p.style.display = ''; });
186
+ if (btn) btn.innerHTML = ICON_EXPAND;
187
+ } else {
188
+ target.classList.add('expanded');
189
+ panes.forEach((p, i) => { if (i !== paneIdx) p.style.display = 'none'; });
190
+ if (btn) btn.innerHTML = ICON_COLLAPSE;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * After streaming finishes, check for HTML code in the response.
196
+ * If found, show the play button in the header. User clicks to run.
197
+ */
198
+ function _autoPreviewHtml(paneIdx, accumulated) {
199
+ if (!accumulated) return;
200
+ const htmlCode = _extractHtmlFromText(accumulated);
201
+ if (!htmlCode) return;
202
+
203
+ const iframe = document.getElementById('cmp-iframe-' + paneIdx);
204
+ const previewBtn = document.getElementById('cmp-preview-' + paneIdx);
205
+ if (!iframe || !previewBtn) return;
206
+
207
+ // Store the HTML on the iframe for when user clicks play
208
+ iframe._htmlCode = htmlCode;
209
+
210
+ // Show the play button
211
+ previewBtn.style.display = '';
212
+ previewBtn.innerHTML = ICON_PLAY;
213
+ previewBtn.title = 'Run preview';
214
+ }
215
+
216
+ /** Toggle between iframe preview and code view for a pane. */
217
+ function togglePanePreview(paneIdx) {
218
+ const iframe = document.getElementById('cmp-iframe-' + paneIdx);
219
+ const hist = document.getElementById('cmp-history-' + paneIdx);
220
+ const btn = document.getElementById('cmp-preview-' + paneIdx);
221
+ if (!iframe || !hist || !btn) return;
222
+
223
+ const showingPreview = iframe.style.display !== 'none';
224
+ if (showingPreview) {
225
+ // Switch to code view
226
+ iframe.style.display = 'none';
227
+ hist.style.display = '';
228
+ btn.innerHTML = ICON_PLAY;
229
+ btn.title = 'Run preview';
230
+ btn.classList.remove('active');
231
+ } else {
232
+ // Switch to preview — load on first click
233
+ if (iframe._htmlCode) iframe.srcdoc = iframe._htmlCode;
234
+ iframe.style.display = '';
235
+ hist.style.display = 'none';
236
+ btn.innerHTML = ICON_CODE;
237
+ btn.title = 'Show code';
238
+ btn.classList.add('active');
239
+ }
240
+ }
241
+
242
+ /** Extract full HTML document from raw accumulated text. */
243
+ function _extractHtmlFromText(text) {
244
+ // 1. Try markdown code fences
245
+ const fenceRe = /`{3,}(?:html)?\s*\r?\n([\s\S]*?)`{3,}/gi;
246
+ let match;
247
+ while ((match = fenceRe.exec(text)) !== null) {
248
+ const code = match[1].trim();
249
+ if (/<!doctype\s+html|<html[\s>]/i.test(code)) return code;
250
+ }
251
+ // 2. Bare HTML
252
+ const bare = text.match(/(<!doctype\s+html[\s\S]*<\/html>)/i)
253
+ || text.match(/(<html[\s>][\s\S]*<\/html>)/i);
254
+ if (bare) return bare[1].trim();
255
+ return null;
256
+ }
257
+
258
+ async function copyPaneResponse(paneIdx) {
259
+ const hist = document.getElementById('cmp-history-' + paneIdx);
260
+ if (!hist) return;
261
+ const aiMsgs = hist.querySelectorAll('.msg-ai');
262
+ if (aiMsgs.length === 0) return;
263
+ const lastAi = aiMsgs[aiMsgs.length - 1];
264
+ // For image panes, copy the prompt text
265
+ const text = lastAi._imageData ? (lastAi._imageData.prompt || '') : (lastAi.querySelector('.body')?.textContent || '');
266
+ try { await navigator.clipboard.writeText(text); }
267
+ catch (e) {
268
+ const ta = document.createElement('textarea');
269
+ ta.value = text; document.body.appendChild(ta); ta.select();
270
+ document.execCommand('copy'); ta.remove();
271
+ }
272
+ if (uiModule) uiModule.showToast(lastAi._imageData ? 'Prompt copied!' : 'Copied!');
273
+ }
274
+
275
+ // ── Add / create / remove panes ──
276
+
277
+ /** Show a model picker dropdown anchored to the "+" button in the pane header. */
278
+ async function _addPane(anchorBtn) {
279
+ if (state._streaming) return;
280
+ const _effectiveType = (state._compareMode === 'agent' || state._compareMode === 'research') ? 'chat' : state._compareMode;
281
+ const filtered = state._cachedModels.filter(m => m.type === _effectiveType);
282
+ if (!filtered.length) return;
283
+
284
+ // Toggle existing dropdown
285
+ const existing = document.querySelector('.add-pane-dropdown');
286
+ if (existing) { if (typeof existing._dismiss === 'function') existing._dismiss(); else existing.remove(); return; }
287
+
288
+ const dropdown = document.createElement('div');
289
+ dropdown.className = 'add-pane-dropdown';
290
+ let closeMenu = () => dropdown.remove();
291
+
292
+ // Search input for large model lists
293
+ if (filtered.length >= 5) {
294
+ const searchInput = document.createElement('input');
295
+ searchInput.type = 'text';
296
+ searchInput.placeholder = 'Search models\u2026';
297
+ searchInput.className = 'add-pane-search';
298
+ searchInput.addEventListener('input', () => {
299
+ const q = searchInput.value.toLowerCase().trim();
300
+ dropdown.querySelectorAll('.pane-model-item').forEach(item => {
301
+ item.style.display = item.textContent.toLowerCase().includes(q) ? '' : 'none';
302
+ });
303
+ });
304
+ searchInput.addEventListener('click', (e) => e.stopPropagation());
305
+ searchInput.addEventListener('keydown', (e) => {
306
+ if (e.key === 'Enter') {
307
+ e.preventDefault();
308
+ const first = dropdown.querySelector('.pane-model-item:not([style*="display: none"])');
309
+ if (first) first.click();
310
+ }
311
+ });
312
+ dropdown.appendChild(searchInput);
313
+ // Desktop: auto-focus the search box so the user can start typing.
314
+ // Mobile: skip — auto-focus pops the on-screen keyboard and covers
315
+ // the model list. The user can tap the search box if they want to
316
+ // filter, otherwise they just tap a model directly.
317
+ if (window.innerWidth > 768) setTimeout(() => searchInput.focus(), 0);
318
+ }
319
+
320
+ filtered.forEach(m => {
321
+ const item = document.createElement('button');
322
+ item.type = 'button';
323
+ item.className = 'pane-model-item';
324
+ const label = m.endpointName ? m.name + ' (' + m.endpointName + ')' : m.name;
325
+ item.textContent = label;
326
+ const alreadyUsed = state._selectedModels.some(s => s.model === m.id && s.endpointId === m.endpointId);
327
+ if (alreadyUsed) item.classList.add('current');
328
+
329
+ item.addEventListener('click', async (e) => {
330
+ e.stopPropagation();
331
+ closeMenu();
332
+ await _createAndAppendPane(m);
333
+ });
334
+ dropdown.appendChild(item);
335
+ });
336
+
337
+ // Position dropdown relative to the viewport (position: fixed) so it
338
+ // can't end up off-screen even when the toolbar has scrolled or the
339
+ // chat-container is wider than the viewport.
340
+ const btnRect = anchorBtn.getBoundingClientRect();
341
+ dropdown.style.position = 'fixed';
342
+ const vw = window.innerWidth;
343
+ const vh = window.innerHeight;
344
+ const margin = 8;
345
+ // Render off-screen first so we can measure the dropdown's actual size.
346
+ // Clamp the width to the viewport up front so long model names can't push
347
+ // the dropdown off the screen edge, and lift z-index above the panes.
348
+ dropdown.style.left = '-9999px';
349
+ dropdown.style.top = '0';
350
+ dropdown.style.maxWidth = (vw - margin * 2) + 'px';
351
+ dropdown.style.zIndex = '100000';
352
+ document.body.appendChild(dropdown);
353
+ const ddRect = dropdown.getBoundingClientRect();
354
+ const ddW = ddRect.width;
355
+ const ddH = ddRect.height;
356
+ // Horizontal: align dropdown's right edge with the button's, then
357
+ // clamp so the dropdown stays within [margin, vw - margin].
358
+ let left = btnRect.right - ddW;
359
+ if (left + ddW > vw - margin) left = vw - margin - ddW;
360
+ if (left < margin) left = margin;
361
+ // Vertical: drop below the button if there's room, otherwise above.
362
+ const spaceBelow = vh - btnRect.bottom;
363
+ const spaceAbove = btnRect.top;
364
+ let top;
365
+ if (spaceBelow >= ddH + margin || spaceBelow >= spaceAbove) {
366
+ top = Math.min(btnRect.bottom + 4, vh - margin - Math.min(ddH, vh - margin * 2));
367
+ } else {
368
+ top = Math.max(margin, btnRect.top - 4 - ddH);
369
+ }
370
+ dropdown.style.left = left + 'px';
371
+ dropdown.style.top = top + 'px';
372
+ dropdown.style.right = 'auto';
373
+ dropdown.style.bottom = 'auto';
374
+ dropdown.style.maxHeight = Math.min(ddH, vh - margin * 2) + 'px';
375
+
376
+ // Close on outside click or Escape (the latter via the registry).
377
+ closeMenu = bindMenuDismiss(dropdown, () => dropdown.remove(), (e) => !dropdown.contains(e.target) && e.target !== anchorBtn);}
378
+
379
+ /** Create a new pane for the given model and append it to the compare grid. */
380
+ async function _createAndAppendPane(m) {
381
+ const i = state._selectedModels.length; // New index
382
+
383
+ // Create session
384
+ const fd = new FormData();
385
+ // Blind mode: neutral slot name only — never leak the model (issue #1285).
386
+ fd.append('name', '[CMP] ' + (state._blindMode ? 'Model ' + _slotChar(i) : m.name));
387
+ fd.append('endpoint_url', m.url || '');
388
+ fd.append('model', m.id || '');
389
+ if (m.endpointId) {
390
+ fd.append('endpoint_id', m.endpointId);
391
+ fd.append('skip_validation', 'true');
392
+ }
393
+ const res = await fetch(`${state.API_BASE}/api/session`, { method: 'POST', body: fd });
394
+ if (!res.ok) return;
395
+ const data = await res.json();
396
+
397
+ // Update arrays
398
+ state._selectedModels.push({ model: m.id, endpoint: m.url, endpointId: m.endpointId, name: m.name, endpointName: m.endpointName || '' });
399
+ state._paneSessionIds.push(data.id);
400
+ state._paneMetrics.push(null);
401
+ state._abortControllers.push(null);
402
+ _persistSelections();
403
+ if (window._updateCheckBtnState) window._updateCheckBtnState();
404
+
405
+ // Build pane DOM
406
+ const label = state._blindMode ? 'Model ' + _slotChar(i) : m.name;
407
+ const pane = document.createElement('div');
408
+ pane.className = 'compare-pane';
409
+ pane.dataset.pane = String(i);
410
+ pane.innerHTML =
411
+ '<div class="pane-header">' +
412
+ '<button class="pane-title pane-title-btn" id="cmp-title-' + i + '" data-pane="' + i + '" type="button">' + escapeHtml(label) + ' <span class="pane-title-caret">&#x25BE;</span></button>' +
413
+ '<span class="pane-timer" id="cmp-timer-' + i + '"></span>' +
414
+ '<span class="pane-finish-badge" id="cmp-badge-' + i + '"></span>' +
415
+ '<div class="pane-actions">' +
416
+ '<button class="pane-action-btn pane-preview-btn" data-action="preview" data-pane="' + i + '" id="cmp-preview-' + i + '" title="Run preview" style="display:none;">' + ICON_PLAY + '</button>' +
417
+ '<button class="pane-action-btn" data-action="reroll" data-pane="' + i + '" title="Re-roll">' + ICON_REROLL + '</button>' +
418
+ '<button class="pane-action-btn" data-action="copy" data-pane="' + i + '" title="Copy">' + ICON_COPY + '</button>' +
419
+ '<button class="pane-action-btn" data-action="expand" data-pane="' + i + '" title="Expand">' + ICON_EXPAND + '</button>' +
420
+ '<button class="pane-action-btn pane-close-btn" data-action="close" data-pane="' + i + '" title="Remove pane">' + ICON_CLOSE + '</button>' +
421
+ '</div>' +
422
+ '</div>' +
423
+ '<div class="chat-history" id="cmp-history-' + i + '"></div>' +
424
+ '<iframe class="compare-pane-iframe" id="cmp-iframe-' + i + '" sandbox="allow-scripts" style="display:none;"></iframe>' +
425
+ '<div class="pane-vote-footer">' +
426
+ '<button class="pane-vote-btn" data-pane="' + i + '" type="button" disabled style="opacity:0.4;">' +
427
+ '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:4px;vertical-align:-2px;"><polyline points="20 6 9 17 4 12"/></svg>' +
428
+ '<span class="pane-vote-label">Vote ' + escapeHtml(label) + '</span>' +
429
+ '</button>' +
430
+ '</div>';
431
+
432
+ // Append to grid
433
+ const grid = document.querySelector('.compare-grid');
434
+ grid.appendChild(pane);
435
+
436
+ // Update grid columns
437
+ const n = state._selectedModels.length;
438
+ grid.dataset.cols = String(Math.min(n, 4));
439
+
440
+ // Update header label
441
+ const headerSpan = document.querySelector('.compare-active > div:first-child span');
442
+ if (headerSpan) {
443
+ const modeLabel = ({ search: ' search providers', agent: ' agents', research: ' research models' }[state._compareMode] || ' models');
444
+ headerSpan.textContent = 'Comparing' + modeLabel +
445
+ (state._blindMode ? ' (blind)' : '') + ' \u00b7 ' + state._timeout + 's timeout';
446
+ }
447
+
448
+ // Rebuild vote bar
449
+ buildVoteBar(n);
450
+
451
+ // Prompt to shuffle in blind mode — tooltip bubble next to Shuffle button
452
+ if (state._blindMode && n > 2) {
453
+ const shuffleBtn = document.getElementById('compare-shuffle-btn');
454
+ if (shuffleBtn) {
455
+ const bubble = document.createElement('div');
456
+ bubble.style.cssText = 'position:absolute;top:100%;right:0;margin-top:6px;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:5px 10px;font-size:11px;white-space:nowrap;z-index:10000;box-shadow:0 4px 12px rgba(0,0,0,0.25);pointer-events:none;opacity:0;transition:opacity 0.2s;';
457
+ bubble.textContent = 'Shuffle models?';
458
+ shuffleBtn.style.position = 'relative';
459
+ shuffleBtn.appendChild(bubble);
460
+ requestAnimationFrame(() => { bubble.style.opacity = '1'; });
461
+ setTimeout(() => { bubble.style.opacity = '0'; setTimeout(() => bubble.remove(), 200); }, 4000);
462
+ }
463
+ }
464
+ }
465
+
466
+ /** Remove a pane from the compare grid. If only 1 remains, exit compare mode. */
467
+ function _removePane(paneIdx) {
468
+ if (state._streaming) return;
469
+
470
+ // Abort if streaming
471
+ if (state._abortControllers[paneIdx]) state._abortControllers[paneIdx].abort();
472
+
473
+ // Delete the session
474
+ const sid = state._paneSessionIds[paneIdx];
475
+ if (sid) {
476
+ fetch(`${state.API_BASE}/api/session/${sid}`, { method: 'DELETE' }).catch(() => {});
477
+ }
478
+
479
+ // Remove from arrays
480
+ state._selectedModels.splice(paneIdx, 1);
481
+ state._paneSessionIds.splice(paneIdx, 1);
482
+ state._paneMetrics.splice(paneIdx, 1);
483
+ state._abortControllers.splice(paneIdx, 1);
484
+ _persistSelections();
485
+ if (window._updateCheckBtnState) window._updateCheckBtnState();
486
+
487
+ // If no panes left, exit compare mode
488
+ if (state._selectedModels.length === 0) {
489
+ if (_deactivate) _deactivate(true);
490
+ return;
491
+ }
492
+
493
+ // Rebuild pane DOM — re-index all panes so IDs stay consistent
494
+ const grid = document.querySelector('.compare-grid');
495
+ grid.querySelectorAll('.compare-pane').forEach(p => p.remove());
496
+
497
+ const n = state._selectedModels.length;
498
+ for (let i = 0; i < n; i++) {
499
+ const label = state._blindMode ? 'Model ' + _slotChar(i) : state._selectedModels[i].name;
500
+ const pane = document.createElement('div');
501
+ pane.className = 'compare-pane';
502
+ pane.dataset.pane = String(i);
503
+ pane.innerHTML =
504
+ '<div class="pane-header">' +
505
+ '<button class="pane-title pane-title-btn" id="cmp-title-' + i + '" data-pane="' + i + '" type="button">' + escapeHtml(label) + ' <span class="pane-title-caret">&#x25BE;</span></button>' +
506
+ '<span class="pane-timer" id="cmp-timer-' + i + '"></span>' +
507
+ '<span class="pane-finish-badge" id="cmp-badge-' + i + '"></span>' +
508
+ '<div class="pane-actions">' +
509
+ '<button class="pane-action-btn pane-stop-btn" data-action="stop" data-pane="' + i + '" title="Stop" style="display:none;"><svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2"/></svg></button>' +
510
+ '<button class="pane-action-btn pane-preview-btn" data-action="preview" data-pane="' + i + '" id="cmp-preview-' + i + '" title="Run preview" style="display:none;">' + ICON_PLAY + '</button>' +
511
+ '<button class="pane-action-btn pane-needs-response" data-action="reroll" data-pane="' + i + '" title="Re-roll" style="display:none;">' + ICON_REROLL + '</button>' +
512
+ '<button class="pane-action-btn pane-needs-response" data-action="copy" data-pane="' + i + '" title="Copy" style="display:none;">' + ICON_COPY + '</button>' +
513
+ '<button class="pane-action-btn" data-action="expand" data-pane="' + i + '" title="Expand">' + ICON_EXPAND + '</button>' +
514
+ '<button class="pane-action-btn pane-close-btn" data-action="close" data-pane="' + i + '" title="Remove pane">' + ICON_CLOSE + '</button>' +
515
+ '</div>' +
516
+ '</div>' +
517
+ '<div class="chat-history" id="cmp-history-' + i + '"></div>' +
518
+ '<iframe class="compare-pane-iframe" id="cmp-iframe-' + i + '" sandbox="allow-scripts" style="display:none;"></iframe>' +
519
+ '<div class="pane-vote-footer">' +
520
+ '<button class="pane-vote-btn" data-pane="' + i + '" type="button" disabled style="opacity:0.4;">' +
521
+ '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right:4px;vertical-align:-2px;"><polyline points="20 6 9 17 4 12"/></svg>' +
522
+ '<span class="pane-vote-label">Vote ' + escapeHtml(label) + '</span>' +
523
+ '</button>' +
524
+ '</div>';
525
+ grid.appendChild(pane);
526
+ }
527
+
528
+ // Update grid columns
529
+ grid.dataset.cols = String(Math.min(n, 4));
530
+
531
+ // Update header label
532
+ const headerSpan = document.querySelector('.compare-active > div:first-child span');
533
+ if (headerSpan) {
534
+ const modeLabel = ({ search: ' search providers', agent: ' agents', research: ' research models' }[state._compareMode] || ' models');
535
+ headerSpan.textContent = 'Comparing' + modeLabel +
536
+ (state._blindMode ? ' (blind)' : '') + ' \u00b7 ' + state._timeout + 's timeout';
537
+ }
538
+
539
+ // Rebuild vote bar
540
+ buildVoteBar(n);
541
+ }
542
+
543
+ /** Show a dropdown under the pane title to swap the model for that pane. */
544
+ function _showModelSwapDropdown(paneIdx, titleBtn) {
545
+ // Don't allow swaps while streaming
546
+ if (state._streaming) return;
547
+
548
+ // Remove any existing dropdown
549
+ const existing = document.querySelector('.pane-model-dropdown');
550
+ if (existing) { if (typeof existing._dismiss === 'function') existing._dismiss(); else existing.remove(); return; }
551
+
552
+ const _effectiveType = (state._compareMode === 'agent' || state._compareMode === 'research') ? 'chat' : state._compareMode;
553
+ const filtered = state._cachedModels.filter(m => m.type === _effectiveType);
554
+ if (filtered.length === 0) return;
555
+
556
+ const dropdown = document.createElement('div');
557
+ dropdown.className = 'pane-model-dropdown';
558
+ let closeMenu = () => dropdown.remove();
559
+
560
+ filtered.forEach(m => {
561
+ const item = document.createElement('button');
562
+ item.type = 'button';
563
+ item.className = 'pane-model-item';
564
+ const label = m.endpointName ? m.name + ' (' + m.endpointName + ')' : m.name;
565
+ item.textContent = label;
566
+ // Highlight current model
567
+ if (state._selectedModels[paneIdx] && state._selectedModels[paneIdx].model === m.id
568
+ && state._selectedModels[paneIdx].endpointId === m.endpointId) {
569
+ item.classList.add('current');
570
+ }
571
+ item.addEventListener('click', async (e) => {
572
+ e.stopPropagation();
573
+ closeMenu();
574
+
575
+ // Update the model for this pane and persist
576
+ state._selectedModels[paneIdx] = {
577
+ model: m.id, endpoint: m.url, endpointId: m.endpointId, name: m.name,
578
+ };
579
+ _persistSelections();
580
+ if (window._updateCheckBtnState) window._updateCheckBtnState();
581
+
582
+ // Delete old session, create new one
583
+ const oldSid = state._paneSessionIds[paneIdx];
584
+ if (oldSid) {
585
+ fetch(`${state.API_BASE}/api/session/${oldSid}`, { method: 'DELETE' }).catch(() => {});
586
+ }
587
+ const fd = new FormData();
588
+ // Blind mode: neutral slot name only — never leak the model (issue #1285).
589
+ fd.append('name', '[CMP] ' + (state._blindMode ? 'Model ' + _slotChar(paneIdx) : m.name));
590
+ fd.append('endpoint_url', m.url || '');
591
+ fd.append('model', m.id || '');
592
+ if (m.endpointId) {
593
+ fd.append('endpoint_id', m.endpointId);
594
+ fd.append('skip_validation', 'true');
595
+ }
596
+ try {
597
+ const res = await fetch(`${state.API_BASE}/api/session`, { method: 'POST', body: fd });
598
+ const data = await res.json();
599
+ state._paneSessionIds[paneIdx] = data.id;
600
+ } catch (err) {
601
+ console.error('Failed to create session for swapped model:', err);
602
+ }
603
+
604
+ // Update title display
605
+ const titleEl = document.getElementById('cmp-title-' + paneIdx);
606
+ if (titleEl) {
607
+ const displayName = state._blindMode
608
+ ? 'Model ' + _slotChar(paneIdx)
609
+ : m.name;
610
+ titleEl.innerHTML = escapeHtml(displayName) + ' <span class="pane-title-caret">&#x25BE;</span>';
611
+ }
612
+
613
+ // Clear pane history for fresh start
614
+ const hist = document.getElementById('cmp-history-' + paneIdx);
615
+ if (hist) { hist.innerHTML = ''; hist.style.display = ''; }
616
+ const iframe = document.getElementById('cmp-iframe-' + paneIdx);
617
+ if (iframe) { iframe.srcdoc = ''; iframe.style.display = 'none'; iframe._htmlCode = null; }
618
+ const previewBtn = document.getElementById('cmp-preview-' + paneIdx);
619
+ if (previewBtn) { previewBtn.style.display = 'none'; previewBtn.classList.remove('active'); }
620
+ const badge = document.getElementById('cmp-badge-' + paneIdx);
621
+ if (badge) { badge.textContent = ''; badge.style.color = ''; }
622
+ });
623
+ dropdown.appendChild(item);
624
+ });
625
+
626
+ // Position relative to the viewport (fixed) and append to document.body so
627
+ // the dropdown can't be clipped by the narrow pane's overflow or run off the
628
+ // screen edge on mobile (matches the "+" add-pane picker behaviour).
629
+ const rect = titleBtn.getBoundingClientRect();
630
+ const vw = window.innerWidth, vh = window.innerHeight, margin = 8;
631
+ dropdown.style.position = 'fixed';
632
+ dropdown.style.zIndex = '100000';
633
+ dropdown.style.maxWidth = (vw - margin * 2) + 'px';
634
+ dropdown.style.overflowY = 'auto';
635
+ dropdown.style.left = '-9999px';
636
+ dropdown.style.top = '0';
637
+ document.body.appendChild(dropdown);
638
+ const ddRect = dropdown.getBoundingClientRect();
639
+ const ddW = ddRect.width, ddH = ddRect.height;
640
+ let left = rect.left;
641
+ if (left + ddW > vw - margin) left = vw - margin - ddW;
642
+ if (left < margin) left = margin;
643
+ const spaceBelow = vh - rect.bottom, spaceAbove = rect.top;
644
+ let top;
645
+ if (spaceBelow >= ddH + margin || spaceBelow >= spaceAbove) {
646
+ top = Math.min(rect.bottom + 4, vh - margin - Math.min(ddH, vh - margin * 2));
647
+ } else {
648
+ top = Math.max(margin, rect.top - 4 - ddH);
649
+ }
650
+ dropdown.style.left = left + 'px';
651
+ dropdown.style.top = top + 'px';
652
+ dropdown.style.maxHeight = Math.min(ddH, vh - margin * 2) + 'px';
653
+
654
+ // Close on outside click or Escape (the latter via the registry).
655
+ closeMenu = bindMenuDismiss(dropdown, () => dropdown.remove(), (e) => !dropdown.contains(e.target) && e.target !== titleBtn);}
656
+
657
+ // ── Shuffle / reset ──
658
+
659
+ function shufflePanePositions() {
660
+ if (state._streaming) return;
661
+ // Remove shuffle prompt bubble if present
662
+ const shuffleBtn = document.getElementById('compare-shuffle-btn');
663
+ if (shuffleBtn) { const b = shuffleBtn.querySelector('div'); if (b) b.remove(); }
664
+ const n = state._selectedModels.length;
665
+ if (n < 2) return;
666
+
667
+ // Fisher-Yates shuffle to get new order
668
+ const indices = Array.from({ length: n }, (_, i) => i);
669
+ for (let i = indices.length - 1; i > 0; i--) {
670
+ const j = Math.floor(Math.random() * (i + 1));
671
+ [indices[i], indices[j]] = [indices[j], indices[i]];
672
+ }
673
+
674
+ // Reorder internal state
675
+ const newModels = indices.map(i => state._selectedModels[i]);
676
+ const newSessionIds = indices.map(i => state._paneSessionIds[i]);
677
+ const newMetrics = indices.map(i => state._paneMetrics[i]);
678
+
679
+ // Collect pane contents (HTML) before swapping
680
+ const paneContents = [];
681
+ const paneClasses = [];
682
+ for (let i = 0; i < n; i++) {
683
+ const hist = document.getElementById('cmp-history-' + i);
684
+ paneContents.push(hist ? hist.innerHTML : '');
685
+ const pane = document.querySelector(`.compare-pane[data-pane="${i}"]`);
686
+ paneClasses.push(pane ? { winner: pane.classList.contains('winner'), loser: pane.classList.contains('loser') } : {});
687
+ }
688
+
689
+ // Apply shuffled state
690
+ state._selectedModels = newModels;
691
+ state._paneSessionIds = newSessionIds;
692
+ state._paneMetrics = newMetrics;
693
+
694
+ // Spin the shuffle button dice icon
695
+ const shuffleBtn2 = document.getElementById('compare-shuffle-btn');
696
+ if (shuffleBtn2) {
697
+ const diceSvg = shuffleBtn2.querySelector('svg');
698
+ if (diceSvg) {
699
+ diceSvg.style.transition = 'transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)';
700
+ diceSvg.style.transform = 'rotate(360deg)';
701
+ setTimeout(() => { diceSvg.style.transition = ''; diceSvg.style.transform = ''; }, 400);
702
+ }
703
+ }
704
+
705
+ // Shake panes and flash titles
706
+ for (let i = 0; i < n; i++) {
707
+ const pane = document.querySelector(`.compare-pane[data-pane="${i}"]`);
708
+ if (pane) {
709
+ pane.style.animation = 'pane-shake 0.3s ease';
710
+ pane.addEventListener('animationend', () => { pane.style.animation = ''; }, { once: true });
711
+ }
712
+ const titleEl = document.getElementById('cmp-title-' + i);
713
+ if (titleEl) {
714
+ titleEl.style.transition = 'opacity 0.12s ease, transform 0.12s ease';
715
+ titleEl.style.opacity = '0.3';
716
+ titleEl.style.transform = 'scale(0.9)';
717
+ titleEl.innerHTML = '?';
718
+ }
719
+ const hist = document.getElementById('cmp-history-' + i);
720
+ if (hist) {
721
+ hist.style.transition = 'opacity 0.15s ease';
722
+ hist.style.opacity = '0';
723
+ }
724
+ }
725
+
726
+ setTimeout(() => {
727
+ for (let i = 0; i < n; i++) {
728
+ const hist = document.getElementById('cmp-history-' + i);
729
+ const pane = document.querySelector(`.compare-pane[data-pane="${i}"]`);
730
+ const titleEl = document.getElementById('cmp-title-' + i);
731
+ const badge = document.getElementById('cmp-badge-' + i);
732
+ const src = indices[i];
733
+
734
+ if (hist) hist.innerHTML = paneContents[src];
735
+ if (titleEl) {
736
+ const lbl = state._blindMode ? 'Model ' + _slotChar(i) : state._selectedModels[i].name;
737
+ titleEl.innerHTML = escapeHtml(lbl) + ' <span class="pane-title-caret">&#x25BE;</span>';
738
+ titleEl.style.transition = 'opacity 0.25s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1)';
739
+ titleEl.style.opacity = '1';
740
+ titleEl.style.transform = 'scale(1)';
741
+ }
742
+ if (badge) { badge.textContent = ''; badge.style.color = ''; }
743
+ if (pane) {
744
+ pane.classList.toggle('winner', !!paneClasses[src].winner);
745
+ pane.classList.toggle('loser', !!paneClasses[src].loser);
746
+ }
747
+ if (hist) {
748
+ hist.style.transition = 'opacity 0.25s ease';
749
+ hist.style.opacity = '1';
750
+ }
751
+ }
752
+ }, 200);
753
+
754
+ // Re-enable blind mode after shuffle
755
+ state._blindMode = true;
756
+
757
+ // Rebuild vote bar with new labels
758
+ setTimeout(() => buildVoteBar(n), 250);
759
+ }
760
+
761
+ function resetCompare() {
762
+ if (state._streaming) stopAll();
763
+ const n = state._selectedModels.length;
764
+
765
+ // Clear last prompt so vote buttons are disabled until next prompt
766
+ state._lastPrompt = '';
767
+
768
+ // Reset finish badges, titles, winner/loser state
769
+ state._finishOrder = 0;
770
+ state._paneMetrics = new Array(n).fill(null);
771
+ const panes = document.querySelectorAll('.compare-pane');
772
+ for (let i = 0; i < n; i++) {
773
+ const badge = document.getElementById('cmp-badge-' + i);
774
+ if (badge) { badge.textContent = ''; badge.style.color = ''; }
775
+ const titleEl = document.getElementById('cmp-title-' + i);
776
+ if (titleEl) {
777
+ const lbl = state._blindMode ? 'Model ' + _slotChar(i) : state._selectedModels[i].name;
778
+ titleEl.innerHTML = escapeHtml(lbl) + ' <span class="pane-title-caret">&#x25BE;</span>';
779
+ }
780
+ if (panes[i]) { panes[i].classList.remove('winner', 'loser'); }
781
+
782
+ // Clear all messages from pane history
783
+ const hist = document.getElementById('cmp-history-' + i);
784
+ if (hist) { hist.innerHTML = ''; hist.style.display = ''; }
785
+
786
+ // Reset iframe preview
787
+ const iframe = document.getElementById('cmp-iframe-' + i);
788
+ if (iframe) { iframe.srcdoc = ''; iframe.style.display = 'none'; iframe._htmlCode = null; }
789
+ const previewBtn = document.getElementById('cmp-preview-' + i);
790
+ if (previewBtn) { previewBtn.style.display = 'none'; previewBtn.classList.remove('active'); }
791
+ }
792
+
793
+ // Re-enable vote bar
794
+ buildVoteBar(n);
795
+
796
+ // Focus input for next prompt
797
+ const ta = document.getElementById('message');
798
+ if (ta) ta.focus();
799
+ }
800
+
801
+ export {
802
+ registerPaneActions,
803
+ stopAll,
804
+ stopPane,
805
+ rerollPane,
806
+ toggleExpandPane,
807
+ togglePanePreview,
808
+ _autoPreviewHtml,
809
+ _extractHtmlFromText,
810
+ copyPaneResponse,
811
+ _addPane,
812
+ _createAndAppendPane,
813
+ _removePane,
814
+ _showModelSwapDropdown,
815
+ shufflePanePositions,
816
+ resetCompare,
817
+ };