@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,806 @@
1
+ // static/js/markdown.js
2
+
3
+ /**
4
+ * Markdown rendering and content processing utilities
5
+ */
6
+
7
+ import uiModule from './ui.js';
8
+ import { splitTableRow } from './markdown/tableRow.js';
9
+
10
+ var escapeHtml = uiModule.esc;
11
+
12
+ function safeLinkUrl(rawUrl) {
13
+ const url = String(rawUrl || '').trim();
14
+ if (url.startsWith('#')) {
15
+ return /^#[A-Za-z0-9_-]*$/.test(url) ? url : '';
16
+ }
17
+ try {
18
+ const parsed = new URL(url, window.location.origin);
19
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
20
+ return parsed.href;
21
+ }
22
+ } catch (_) {
23
+ return '';
24
+ }
25
+ return '';
26
+ }
27
+
28
+ function linkHtml(text, url) {
29
+ const safeUrl = safeLinkUrl(url);
30
+ const safeText = escapeHtml(text);
31
+ if (!safeUrl) return safeText;
32
+ if (safeUrl.startsWith('#')) {
33
+ return `<a href="${safeUrl}" class="chat-link">${safeText}</a>`;
34
+ }
35
+ return `<a href="${escapeHtml(safeUrl)}" target="_blank" rel="noopener noreferrer">${safeText}</a>`;
36
+ }
37
+
38
+ /**
39
+ * Sanitize the raw-HTML fragments that mdToHtml deliberately preserves from
40
+ * the source text — <details> blocks (collapsible agent output) and <a> tags
41
+ * (emitted by the markdown link pass). Those fragments are later restored
42
+ * verbatim into innerHTML, so without scrubbing them a model — or any content
43
+ * routed through here — could smuggle in an `<img onerror=...>`, an
44
+ * `<a href="javascript:...">`, an `onmouseover=` handler, etc. and execute
45
+ * script in the authenticated page (DOM XSS).
46
+ *
47
+ * Parsing into a <template> is inert: assigning to template.innerHTML neither
48
+ * fetches resources nor runs scripts, so we can walk the resulting tree,
49
+ * drop script-capable elements, and strip event-handler attributes and
50
+ * dangerous URL schemes before the (now safe) fragment is handed back.
51
+ */
52
+ const _ALLOWED_HTML_BAD_TAGS = new Set([
53
+ 'SCRIPT', 'IFRAME', 'OBJECT', 'EMBED', 'LINK', 'META',
54
+ 'STYLE', 'BASE', 'FORM', 'NOSCRIPT', 'TEMPLATE',
55
+ // Foreign-content roots. SVG/MathML have their own parser rules and are a
56
+ // classic mutation-XSS vehicle — e.g. an SVG-namespaced <script>, whose
57
+ // `tagName` is the lower-case 'script' and would slip a name check that
58
+ // assumed HTML's upper-casing. They aren't needed in the <details>/<a>
59
+ // fragments we preserve, so drop the whole subtree.
60
+ 'SVG', 'MATH',
61
+ ]);
62
+ const _ALLOWED_HTML_URL_ATTRS = new Set([
63
+ 'href', 'src', 'xlink:href', 'action', 'formaction', 'background', 'poster',
64
+ ]);
65
+
66
+ function _cleanAllowedHtmlOnce(htmlString) {
67
+ const tpl = document.createElement('template');
68
+ tpl.innerHTML = htmlString;
69
+ for (const el of Array.from(tpl.content.querySelectorAll('*'))) {
70
+ // Upper-case the tag for comparison: HTML tagNames are upper-case, but
71
+ // SVG/MathML elements preserve their original (lower/camel) case, so a
72
+ // raw `Set.has(el.tagName)` would miss e.g. a namespaced <script>.
73
+ if (_ALLOWED_HTML_BAD_TAGS.has(el.tagName.toUpperCase())) {
74
+ el.remove();
75
+ continue;
76
+ }
77
+ for (const attr of Array.from(el.attributes)) {
78
+ const name = attr.name.toLowerCase();
79
+ // Drop every inline event handler (onerror, onclick, onmouseover, ...)
80
+ // and srcdoc (a frame-less script vector).
81
+ if (name.startsWith('on') || name === 'srcdoc') {
82
+ el.removeAttribute(attr.name);
83
+ continue;
84
+ }
85
+ // Neutralize javascript:/vbscript:/data: in URL-bearing attributes.
86
+ // Strip control/space chars first so e.g. "java\tscript:" can't slip by.
87
+ if (_ALLOWED_HTML_URL_ATTRS.has(name)) {
88
+ const value = (attr.value || '').replace(/[\x00-\x20]+/g, '').toLowerCase();
89
+ if (/^(javascript|vbscript|data):/.test(value)) {
90
+ el.removeAttribute(attr.name);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ return tpl.innerHTML;
96
+ }
97
+
98
+ function sanitizeAllowedHtml(html) {
99
+ const raw = String(html == null ? '' : html);
100
+ // Non-browser context (e.g. a future SSR/Node import): fail closed by
101
+ // escaping rather than trusting the markup.
102
+ if (typeof document === 'undefined') return escapeHtml(raw);
103
+
104
+ // Sanitize to a fixpoint. Re-parsing the serialized output can mutate the
105
+ // tree (the basis of mutation-XSS), so re-clean until it stops changing.
106
+ let out = raw;
107
+ for (let i = 0; i < 4; i++) {
108
+ const next = _cleanAllowedHtmlOnce(out);
109
+ if (next === out) break;
110
+ out = next;
111
+ }
112
+ return out;
113
+ }
114
+
115
+ /**
116
+ * Check if text has unclosed think tag
117
+ */
118
+ export function hasUnclosedThinkTag(text) {
119
+ const openCount = (text.match(/<think(?:ing)?>/gi) || []).length;
120
+ const closeCount = (text.match(/<\/think(?:ing)?>/gi) || []).length;
121
+ return openCount > closeCount;
122
+ }
123
+
124
+ export function startsWithReasoningPrefix(text) {
125
+ return /^\s*(?:thinking(?:\s+process)?\s*:|the user |i need |i should |i will |they are |the question |i can )/i.test(text || '');
126
+ }
127
+
128
+ function normalizePlainThinking(text) {
129
+ if (!text || /<think/i.test(text)) return text;
130
+
131
+ const trimmed = text.trimStart();
132
+ if (!startsWithReasoningPrefix(trimmed)) return text;
133
+
134
+ const replyStarts = [
135
+ 'Hey', 'Hi ', 'Hi!', 'Hello', 'Sure', 'Yes', 'No ', 'No,', 'Yo', 'OK',
136
+ 'Here', 'Absolutely', 'Of course', 'Great', 'Alright', 'Thanks', 'Welcome',
137
+ 'Good ', "I'm happy", "I'd be"
138
+ ];
139
+ const prefixRegex = /^(thinking(?:\s+process)?\s*:)\s*/i;
140
+ const escapedReplyStarts = replyStarts.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
141
+ const boundaryRegex = new RegExp(
142
+ `^([\\s\\S]*?)(\\n\\n(?=${escapedReplyStarts.join('|')}|I |What|Let|This |As ))[\\s\\S]*$`,
143
+ 'i'
144
+ );
145
+ const boundaryMatch = boundaryRegex.exec(trimmed);
146
+
147
+ if (boundaryMatch) {
148
+ const thinkBlock = boundaryMatch[1].replace(prefixRegex, '').trim();
149
+ const reply = trimmed.slice(boundaryMatch[1].length).trimStart();
150
+ if (thinkBlock && reply) return `<think>${thinkBlock}</think>\n\n${reply}`;
151
+ }
152
+
153
+ const lines = trimmed.split('\n');
154
+ for (let index = 1; index < lines.length; index += 1) {
155
+ const line = lines[index].trim();
156
+ if (!line) continue;
157
+ if (replyStarts.some((prefix) => line.startsWith(prefix))) {
158
+ const thinkBlock = lines.slice(0, index).join('\n').replace(prefixRegex, '').trim();
159
+ const reply = lines.slice(index).join('\n').trim();
160
+ if (thinkBlock && reply) return `<think>${thinkBlock}</think>\n${reply}`;
161
+ }
162
+ }
163
+
164
+ const withoutPrefix = trimmed.replace(prefixRegex, '');
165
+ for (const prefix of replyStarts) {
166
+ const rx = new RegExp(`[.!?]\\s*(${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`);
167
+ const match = rx.exec(withoutPrefix);
168
+ if (match && match.index > 20) {
169
+ const thinkBlock = withoutPrefix.slice(0, match.index + 1).trim();
170
+ const reply = withoutPrefix.slice(match.index + 1).trim();
171
+ if (thinkBlock && reply) return `<think>${thinkBlock}</think>\n${reply}`;
172
+ }
173
+ }
174
+
175
+ return text;
176
+ }
177
+
178
+ /**
179
+ * Extract all complete thinking blocks and remaining content
180
+ */
181
+ export function extractThinkingBlocks(text) {
182
+ // Handle malformed patterns: <think></think>\n...actual thinking...\n</think>
183
+ // Some models emit an empty <think></think> then put thinking text outside,
184
+ // closed by a second orphaned </think>.
185
+ let normalized = normalizePlainThinking(text);
186
+ // Collapse <think>short</think>...real thinking...</think> into one block
187
+ // Models sometimes emit a trivial first block then continue thinking outside tags
188
+ normalized = normalized.replace(/<think(?:ing)?(?:\s+[^>]*)?>.{0,30}<\/think(?:ing)?>\s*([\s\S]*?)<\/think(?:ing)?>/gi, (m, content) => {
189
+ return '<think>' + content.trim() + '</think>';
190
+ });
191
+
192
+ // Merge consecutive <think> blocks (some models split thinking across multiple tags)
193
+ normalized = normalized.replace(/<\/think(?:ing)?>\s*<think(?:ing)?(?:\s+[^>]*)?>/gi, '\n\n');
194
+
195
+ // Extract thinking time attribute if present
196
+ const timeMatch = normalized.match(/<think(?:ing)?\s+time="([\d.]+)"/i);
197
+ const thinkingTime = timeMatch ? timeMatch[1] : null;
198
+ // Strip time attribute for content extraction
199
+ normalized = normalized.replace(/<think(?:ing)?\s+time="[\d.]+"/gi, '<think');
200
+
201
+ const thinkRegex = /<think(?:ing)?(?:\s+[^>]*)?>([\s\S]*?)<\/think(?:ing)?>/gi;
202
+ const thinkingBlocks = [];
203
+ let match;
204
+
205
+ // Extract all complete thinking blocks
206
+ while ((match = thinkRegex.exec(normalized)) !== null) {
207
+ const content = match[1].trim();
208
+ if (content) thinkingBlocks.push(content);
209
+ }
210
+
211
+ // Remove all complete <think>/<thinking> blocks
212
+ let cleanContent = normalized.replace(thinkRegex, '');
213
+
214
+ // If there's an unclosed tag, decide between two cases:
215
+ // (a) Stray opener at the very start with no real reply before it — typical
216
+ // of quantized models (MiniMax-AWQ) that emit a literal `<think>` token
217
+ // at the start of every reply without ever closing it. Strip just the
218
+ // opener and keep the body as the reply, otherwise the bubble looks
219
+ // blank on reload (the body was being treated as collapsed thinking).
220
+ // (b) Cut-off mid-generation — there's already real reply text before the
221
+ // opener. Drop from the tag onward as before (it's truncated thinking).
222
+ if (hasUnclosedThinkTag(normalized)) {
223
+ const strayOpener = cleanContent.match(/^\s*<think(?:ing)?(?:\s+[^>]*)?>([\s\S]*)$/i);
224
+ if (strayOpener) {
225
+ cleanContent = strayOpener[1];
226
+ } else {
227
+ cleanContent = cleanContent.replace(/<think(?:ing)?(?:\s+[^>]*)?>[\s\S]*$/gi, '');
228
+ }
229
+ }
230
+
231
+ // Handle orphaned </think> with no opening tag — text before it is leaked thinking
232
+ const orphanMatch = cleanContent.match(/^([\s\S]+?)<\/think(?:ing)?>/i);
233
+ if (orphanMatch && orphanMatch[1].trim()) {
234
+ thinkingBlocks.push(orphanMatch[1].trim());
235
+ cleanContent = cleanContent.slice(orphanMatch[0].length);
236
+ }
237
+
238
+ // Strip any remaining orphaned closing tags
239
+ cleanContent = cleanContent.replace(/<\/think(?:ing)?>/gi, '');
240
+
241
+ // Merge all thinking blocks into one — no reason to show multiple dropdowns
242
+ const mergedBlocks = thinkingBlocks.length > 1
243
+ ? [thinkingBlocks.join('\n\n')]
244
+ : thinkingBlocks;
245
+
246
+ return {
247
+ thinkingBlocks: mergedBlocks,
248
+ content: cleanContent.trim(),
249
+ thinkingTime,
250
+ };
251
+ }
252
+
253
+ /**
254
+ * Create a collapsible thinking section
255
+ */
256
+ function createThinkingSection(thinkingContent, index = 0, thinkingTime = null) {
257
+ const id = `thinking-${Date.now()}-${index}`;
258
+ const timeHtml = thinkingTime ? `<span style="font-size:11px;opacity:0.4;font-variant-numeric:tabular-nums;">${thinkingTime}s</span>` : '';
259
+ return `
260
+ <div class="thinking-section">
261
+ <div class="thinking-header" data-thinking-id="${id}">
262
+ <div class="thinking-header-left">
263
+ <span>View thinking process</span>
264
+ </div>
265
+ <div style="display:flex;align-items:center;gap:6px;">
266
+ ${timeHtml}
267
+ <span class="thinking-toggle" id="${id}-toggle"></span>
268
+ </div>
269
+ </div>
270
+ <div class="thinking-content" id="${id}">
271
+ <div class="thinking-content-inner">
272
+ ${mdToHtml(thinkingContent)}
273
+ </div>
274
+ </div>
275
+ </div>
276
+ `;
277
+ }
278
+
279
+ /**
280
+ * Process text and render with thinking sections
281
+ */
282
+ // ── Emoji → monochrome SVG (OpenMoji-black via same-origin /api/emoji proxy) ──
283
+ // Replace colorful system/Twemoji emoji with single-color line icons tinted to
284
+ // the surrounding text color (project rule: never colorful emoji). Operates on
285
+ // rendered HTML: only touches text outside tags and skips <code>/<pre>.
286
+ const _EMOJI_RE = /\p{Extended_Pictographic}/u;
287
+ const _emojiSeg = (typeof Intl !== 'undefined' && Intl.Segmenter)
288
+ ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) : null;
289
+
290
+ function _emojiCodepoints(emoji) {
291
+ // Twemoji filename rule: strip U+FE0F unless the sequence has a ZWJ (U+200D).
292
+ const s = emoji.indexOf('‍') >= 0 ? emoji : emoji.replace(/️/g, '');
293
+ const cps = [];
294
+ for (const ch of s) { const c = ch.codePointAt(0); if (c) cps.push(c.toString(16)); }
295
+ return cps.join('-');
296
+ }
297
+ function _emojiImg(emoji) {
298
+ const code = _emojiCodepoints(emoji);
299
+ if (!code) return emoji;
300
+ // Monochrome line icon: the OpenMoji black SVG is used as a CSS mask filled
301
+ // with the surrounding text color (currentColor), so emoji render as a single
302
+ // theme-tinted line glyph — never colorful (project rule). If the proxy can't
303
+ // supply the glyph it returns a transparent SVG, so the mask shows nothing.
304
+ return `<span class="emoji" role="img" aria-label="${emoji}" style="--em:url('/api/emoji/${code}.svg')"></span>`;
305
+ }
306
+ function _svgifyText(text) {
307
+ if (!_emojiSeg) return text;
308
+ let out = '';
309
+ for (const { segment } of _emojiSeg.segment(text)) {
310
+ out += _EMOJI_RE.test(segment) ? _emojiImg(segment) : segment;
311
+ }
312
+ return out;
313
+ }
314
+ /** When "Text-only Emojis" is on, keep Unicode in HTML so deEmojify() can strip them. */
315
+ function _useSvgEmoji() {
316
+ return typeof document === 'undefined' || !document.body?.classList.contains('text-emojis');
317
+ }
318
+
319
+ export function svgifyEmoji(html) {
320
+ if (!_useSvgEmoji() || !html || !_EMOJI_RE.test(html)) return html;
321
+ const parts = html.split(/(<[^>]*>)/); // odd indices = tags
322
+ let codeDepth = 0;
323
+ for (let i = 0; i < parts.length; i++) {
324
+ if (i % 2 === 1) {
325
+ const t = parts[i].toLowerCase();
326
+ if (/^<(pre|code)[\s>]/.test(t)) codeDepth++;
327
+ else if (/^<\/(pre|code)\s*>/.test(t)) codeDepth = Math.max(0, codeDepth - 1);
328
+ continue;
329
+ }
330
+ if (codeDepth === 0 && _EMOJI_RE.test(parts[i])) parts[i] = _svgifyText(parts[i]);
331
+ }
332
+ return parts.join('');
333
+ }
334
+ /**
335
+ * Generic collapsible section that reuses the thinking-dropdown styling and its
336
+ * delegated toggle (any `.thinking-header[data-thinking-id]`). The label drives
337
+ * the "View <label>" / "Hide <label>" text via data-label. Used e.g. for the
338
+ * vision-model image description on a user's photo message.
339
+ */
340
+ export function createCollapsible(contentMarkdown, label = 'details') {
341
+ const id = `collapse-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
342
+ const safeLabel = escapeHtml(label);
343
+ return `
344
+ <div class="thinking-section">
345
+ <div class="thinking-header" data-thinking-id="${id}">
346
+ <div class="thinking-header-left"><span data-label="${safeLabel}">View ${safeLabel}</span></div>
347
+ <div style="display:flex;align-items:center;gap:6px;"><span class="thinking-toggle" id="${id}-toggle"></span></div>
348
+ </div>
349
+ <div class="thinking-content" id="${id}"><div class="thinking-content-inner">${mdToHtml(contentMarkdown)}</div></div>
350
+ </div>`;
351
+ }
352
+
353
+ export function processWithThinking(text) {
354
+ const { thinkingBlocks, content, thinkingTime } = extractThinkingBlocks(text);
355
+
356
+ let html = '';
357
+
358
+ // Add thinking sections (collapsed by default)
359
+ thinkingBlocks.forEach((block, index) => {
360
+ html += createThinkingSection(block, index, thinkingTime);
361
+ });
362
+
363
+ // Add the actual content
364
+ if (content) {
365
+ html += mdToHtml(content);
366
+ }
367
+
368
+ return _useSvgEmoji() ? svgifyEmoji(html) : html;
369
+ }
370
+
371
+ /**
372
+ * Convert markdown to HTML
373
+ */
374
+ export function mdToHtml(src) {
375
+ const allowedHtmlBlocks = [];
376
+ const codeBlocks = [];
377
+ const mermaidBlocks = [];
378
+ let s = (src ?? '');
379
+
380
+ // Extract fenced code blocks before any markdown/HTML preservation passes.
381
+ // Otherwise placeholders from the allowed-HTML sanitizer (e.g.
382
+ // ___ALLOWED_HTML_0___) can leak into quoted HTML/JS samples, because the
383
+ // placeholder gets captured as literal code content and never restored inside
384
+ // the final <pre><code> block.
385
+ s = s.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => {
386
+ const cleaned = code
387
+ .replace(/\r\n/g, '\n')
388
+ .replace(/[ \t]+$/gm, '')
389
+ .replace(/^\s*\n+/, '')
390
+ .replace(/\n+\s*$/g, '');
391
+
392
+ // Mermaid diagrams: render as diagram instead of code block
393
+ if (lang && lang.toLowerCase() === 'mermaid') {
394
+ const mermaidId = 'mermaid-' + Date.now() + '-' + mermaidBlocks.length;
395
+ const raw = cleaned.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
396
+ const placeholder = `___MERMAID_BLOCK_${mermaidBlocks.length}___`;
397
+ mermaidBlocks.push(`<div class="mermaid-container"><pre class="mermaid" id="${mermaidId}">${escapeHtml(raw)}</pre></div>`);
398
+ return placeholder;
399
+ }
400
+
401
+ const escaped = cleaned.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
402
+ const placeholder = `___CODE_BLOCK_${codeBlocks.length}___`;
403
+
404
+ const langClass = lang ? ` class="language-${lang}"` : '';
405
+ const runnableLangs = ['python','py','javascript','js','html','bash','sh','shell','zsh'];
406
+ const runBtn = (lang && runnableLangs.includes(lang.toLowerCase()))
407
+ ? `<button type="button" class="run-code" data-code="${escapeHtml(escaped)}" data-lang="${lang}" title="Run code"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"/></svg></button>`
408
+ : '';
409
+ const editBtn = `<button type="button" class="edit-code" title="Edit"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></button>`;
410
+ codeBlocks.push(`<pre><code${langClass} data-lang="${lang || ''}">${escapeHtml(escaped)}</code>${runBtn}${editBtn}<button type="button" class="copy-code" data-code="${escapeHtml(escaped)}"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button></pre>`);
411
+
412
+ return placeholder;
413
+ });
414
+
415
+ // Repair common ways the agent mangles the entity-anchor convention
416
+ // (`[Name](#kind-<id>)`). Models reliably get the single-link case
417
+ // right but slip into other formats when listing many in a table.
418
+ // These regexes upgrade the broken forms to proper markdown links so
419
+ // the standard `[text](url)` handler below picks them up.
420
+ const ANCHOR_KIND = '(?:session|document|note|image|email|event|task|skill|research)';
421
+ // Case A: `[Name] [#kind-id]` — agent put the URL in brackets, often
422
+ // in a table cell next to the label. Pair them.
423
+ s = s.replace(
424
+ new RegExp(`\\[([^\\]\\n]+?)\\]\\s*\\[#(${ANCHOR_KIND}-[A-Za-z0-9_-]+)\\]`, 'g'),
425
+ '[$1](#$2)',
426
+ );
427
+ // Case B: bare `[#kind-id]` with no preceding label — give it a
428
+ // generic "→ open" link text so it still renders as a button.
429
+ s = s.replace(
430
+ new RegExp(`\\[#(${ANCHOR_KIND}-[A-Za-z0-9_-]+)\\]`, 'g'),
431
+ '[→ open](#$1)',
432
+ );
433
+ // Case C: bare `#kind-id` in plain text — only when it's word-
434
+ // boundary delimited and NOT already inside a markdown link or
435
+ // anchor syntax. Use a lookbehind for `](` or `[` to skip those.
436
+ s = s.replace(
437
+ new RegExp(`(^|[^\\[(])#(${ANCHOR_KIND}-[A-Za-z0-9_-]+)\\b`, 'g'),
438
+ '$1[#$2](#$2)',
439
+ );
440
+
441
+ // Convert markdown links [text](url) to clickable links
442
+ // Internal #hash links navigate in-page; external links open in new tab
443
+ s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => {
444
+ return linkHtml(text, url);
445
+ });
446
+
447
+ // Autolink bare URLs (http/https). Skips URLs already inside <a> tags
448
+ // (placed by markdown link replacement above) and URLs in backticks.
449
+ s = s.replace(
450
+ /(^|[\s(<])(https?:\/\/[^\s<>"'`\]]+[^\s<>"'`\].,;:!?])/g,
451
+ (match, prefix, url) => `${prefix}${linkHtml(url, url)}`
452
+ );
453
+
454
+ // Autolink scheme-less domains the model often emits as plain text
455
+ // (e.g. "techcrunch.com/ai", "perplexity.ai", "www.wired.com"). The TLD
456
+ // allowlist keeps it from matching file names / versions ("package.json",
457
+ // "node.js", "v1.2.3"); the required start/[\s(<] prefix means domains
458
+ // already inside an http link (preceded by "//") or an email ("@") are
459
+ // skipped. Trailing sentence punctuation is kept outside the link.
460
+ s = s.replace(
461
+ /(^|[\s(<])((?:www\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9-]+)*\.(?:com|org|net|io|ai|co|dev|app|gov|edu|news|info|tech|xyz|me)(?:\/[^\s<>"'`\])]*)?)/gi,
462
+ (match, prefix, domain) => {
463
+ const trail = (domain.match(/[.,;:!?)]+$/) || [''])[0];
464
+ const core = trail ? domain.slice(0, -trail.length) : domain;
465
+ return `${prefix}${linkHtml(core, 'https://' + core)}${trail}`;
466
+ }
467
+ );
468
+
469
+ // Extract <details>...</details> blocks and replace with placeholders
470
+ // Default to open so agent output is visible
471
+ s = s.replace(/<details>([\s\S]*?)<\/details>/gi, (match) => {
472
+ const placeholder = `___ALLOWED_HTML_${allowedHtmlBlocks.length}___`;
473
+ allowedHtmlBlocks.push(sanitizeAllowedHtml(match.replace(/<details>/i, '<details open>')));
474
+ return placeholder;
475
+ });
476
+
477
+ // ALSO preserve <a> tags the same way (they're now in the HTML from markdown conversion)
478
+ s = s.replace(/<a\s+[^>]*>.*?<\/a>/gi, (match) => {
479
+ const placeholder = `___ALLOWED_HTML_${allowedHtmlBlocks.length}___`;
480
+ allowedHtmlBlocks.push(sanitizeAllowedHtml(match));
481
+ return placeholder;
482
+ });
483
+
484
+ // Now escape everything else
485
+ s = s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
486
+
487
+ s = s.replace(/\n{3,}/g, '\n\n');
488
+
489
+ // KaTeX math rendering (after code blocks are extracted, so math in code is safe)
490
+ const mathBlocks = [];
491
+ if (window.katex) {
492
+ // Display math: \[ ... \] — GPT-style delimiter (gpt-5.x, Claude, etc.).
493
+ // Handle before $$/$ so all common delimiters render.
494
+ s = s.replace(/\\\[([\s\S]*?)\\\]/g, (match, math) => {
495
+ try {
496
+ const raw = math.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
497
+ const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
498
+ mathBlocks.push(katex.renderToString(raw.trim(), { displayMode: true, throwOnError: false }));
499
+ return placeholder;
500
+ } catch (e) { return match; }
501
+ });
502
+ // Inline math: \( ... \) — GPT-style inline delimiter. Single-line only
503
+ // ([^\n]) so a stray escaped paren in prose can't swallow across lines.
504
+ s = s.replace(/\\\(([^\n]*?)\\\)/g, (match, math) => {
505
+ try {
506
+ const raw = math.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
507
+ const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
508
+ mathBlocks.push(katex.renderToString(raw.trim(), { displayMode: false, throwOnError: false }));
509
+ return placeholder;
510
+ } catch (e) { return match; }
511
+ });
512
+ // Display math: $$...$$
513
+ s = s.replace(/\$\$([\s\S]*?)\$\$/g, (match, math) => {
514
+ try {
515
+ const raw = math.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
516
+ const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
517
+ mathBlocks.push(katex.renderToString(raw.trim(), { displayMode: true, throwOnError: false }));
518
+ return placeholder;
519
+ } catch (e) { return match; }
520
+ });
521
+ // Inline math: $...$ (not preceded/followed by $ or digit, not spanning multiple lines)
522
+ s = s.replace(/(?<!\$)\$(?!\$)([^\$\n]+?)\$(?!\$)/g, (match, math) => {
523
+ try {
524
+ const raw = math.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>');
525
+ const placeholder = `___MATH_BLOCK_${mathBlocks.length}___`;
526
+ mathBlocks.push(katex.renderToString(raw.trim(), { displayMode: false, throwOnError: false }));
527
+ return placeholder;
528
+ } catch (e) { return match; }
529
+ });
530
+ }
531
+
532
+ // Handle pipe tables
533
+ s = s.replace(/(?:^|\n)([^\n]*\|[^\n]*\|[^\n]*)(?:\n([^\n]*\|[^\n]*\|[^\n]*))*/g, (table) => {
534
+ if (table.includes('___CODE_BLOCK_') || table.includes('___ALLOWED_HTML_')) return table;
535
+
536
+ const rows = table.trim().split('\n');
537
+ if (rows.length < 2) return table;
538
+
539
+ let html = '<table style="border-collapse: collapse; width: 100%; margin: 10px 0;">';
540
+
541
+ rows.forEach((row, idx) => {
542
+ if (idx === 1 && /^[\s|:\-]+$/.test(row)) {
543
+ html += '<tbody>';
544
+ return;
545
+ }
546
+ const cells = splitTableRow(row);
547
+ if (cells.length === 0) return;
548
+
549
+ html += '<tr>';
550
+
551
+ cells.forEach(cell => {
552
+ const tag = idx === 0 ? 'th' : 'td';
553
+ html += `<${tag} style="padding: 8px; text-align: left; border-bottom: 1px solid var(--border);">${cell.trim()}</${tag}>`;
554
+ });
555
+
556
+ html += '</tr>';
557
+ });
558
+
559
+ html += '</tbody></table>';
560
+ return html;
561
+ });
562
+
563
+ // Inline code (but not placeholders)
564
+ s = s.replace(/`([^`]+?)`/g, (match, code) => {
565
+ if (code.startsWith('___CODE_BLOCK_') || code.startsWith('___ALLOWED_HTML_')) return match;
566
+ return `<code>${code}</code>`;
567
+ });
568
+
569
+ // Horizontal rules (must come before bold/italic to avoid * conflicts)
570
+ s = s.replace(/^(?:---|\*\*\*|___)\s*$/gm, '<hr>');
571
+
572
+ // Bold, italic, strikethrough
573
+ s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>').replace(/\*([^*]+)\*/g, '<em>$1</em>');
574
+ s = s.replace(/~~([^~]+)~~/g, '<del>$1</del>');
575
+
576
+ // Headers
577
+ s = s.replace(/^###### (.*)$/gm, '<h6>$1</h6>')
578
+ .replace(/^##### (.*)$/gm, '<h5>$1</h5>')
579
+ .replace(/^#### (.*)$/gm, '<h4>$1</h4>')
580
+ .replace(/^### (.*)$/gm, '<h3>$1</h3>')
581
+ .replace(/^## (.*)$/gm, '<h2>$1</h2>')
582
+ .replace(/^# (.*)$/gm, '<h1>$1</h1>');
583
+
584
+ // Ordered lists (1. 2. 3. etc.)
585
+ s = s.replace(/^(\d+)\. (.*)$/gm, '<oli>$2</oli>');
586
+ s = s.replace(/(?:^|\n)(<oli>[\s\S]*?)(?=\n(?!<oli>)|$)/g, m => `<ol>${m.trim().replace(/<\/?oli>/g, (t) => t === '<oli>' ? '<li>' : '</li>')}</ol>`);
587
+
588
+ // Unordered lists
589
+ s = s.replace(/^(?:- |\* )(.*)$/gm, '<uli>$1</uli>');
590
+ s = s.replace(/(^|\n)((?:<uli>[^\n]*<\/uli>(?:\n|$))+)/g, (_, prefix, block) =>
591
+ `${prefix}<ul>${block.trim().replace(/<\/?uli>/g, (t) => t === '<uli>' ? '<li>' : '</li>')}</ul>`);
592
+
593
+ // Blockquotes
594
+ s = s.replace(/^&gt; (.*)$/gm, '<bq>$1</bq>');
595
+ s = s.replace(/(?:^|\n)(<bq>[\s\S]*?)(?=\n(?!<bq>)|$)/g, m =>
596
+ `<blockquote>${m.trim().replace(/<\/?bq>/g, (t) => t === '<bq>' ? '<p>' : '</p>')}</blockquote>`);
597
+
598
+ // Paragraphs - but NOT for code block placeholders or allowed HTML
599
+ s = s.replace(/^(?!<h\d|<ul>|<ol>|<li>|<oli>|<pre>|<blockquote>|<bq>|<hr>|___CODE_BLOCK_|___ALLOWED_HTML_|___MATH_BLOCK_|___MERMAID_BLOCK_)([^\n]+)$/gm, '<p>$1</p>');
600
+
601
+ // Line breaks within paragraphs
602
+ s = s.replace(/<p>([\s\S]*?)<\/p>/g, (match, content) => {
603
+ if (content.includes('___CODE_BLOCK_') || content.includes('___ALLOWED_HTML_') || content.includes('___MATH_BLOCK_') || content.includes('___MERMAID_BLOCK_')) return match;
604
+ const withLineBreaks = content.replace(/\n{2,}/g, '</p><p>').replace(/\n/g, '<br>');
605
+ return `<p>${withLineBreaks}</p>`;
606
+ });
607
+
608
+ // Remove empty paragraphs
609
+ s = s.replace(/<p><\/p>/g, '');
610
+
611
+ // CRITICAL: Restore allowed HTML blocks first
612
+ allowedHtmlBlocks.forEach((block, index) => {
613
+ s = s.replace(`___ALLOWED_HTML_${index}___`, block);
614
+ });
615
+
616
+ // Restore math blocks
617
+ mathBlocks.forEach((block, index) => {
618
+ s = s.replace(`___MATH_BLOCK_${index}___`, block);
619
+ });
620
+
621
+ // Restore mermaid diagram blocks
622
+ mermaidBlocks.forEach((block, index) => {
623
+ s = s.replace(`___MERMAID_BLOCK_${index}___`, block);
624
+ });
625
+
626
+ // CRITICAL: Restore code blocks at the end
627
+ codeBlocks.forEach((block, index) => {
628
+ s = s.replace(`___CODE_BLOCK_${index}___`, block);
629
+ });
630
+
631
+ return _useSvgEmoji() ? svgifyEmoji(s) : s;
632
+ }
633
+
634
+ /**
635
+ * Reduce excessive whitespace outside of code blocks
636
+ */
637
+ export function squashOutsideCode(s) {
638
+ if (!s) return "";
639
+ const parts = String(s).split(/```/);
640
+ for (let i = 0; i < parts.length; i += 2) {
641
+ parts[i] = parts[i]
642
+ .replace(/\r\n/g, '\n')
643
+ .replace(/[ \t]+\n/g, '\n')
644
+ .replace(/\n{3,}/g, '\n\n');
645
+ }
646
+ return parts.join('```');
647
+ }
648
+
649
+ /**
650
+ * Render content that may be text or array of content blocks
651
+ */
652
+ export function renderContent(content) {
653
+ if (Array.isArray(content)) {
654
+ const texts = [];
655
+ for (const blk of content) {
656
+ if (blk.type === 'text') texts.push(blk.text);
657
+ else if (blk.type === 'image_url') texts.push('[image]');
658
+ }
659
+ return texts.join('\n');
660
+ }
661
+ return content;
662
+ }
663
+
664
+ /**
665
+ * Initialize any unprocessed Mermaid diagrams in a container (or whole document)
666
+ */
667
+ export function renderMermaid(container) {
668
+ if (!window.mermaid) return;
669
+ initMermaid();
670
+ const target = container || document;
671
+ const pending = target.querySelectorAll('pre.mermaid:not([data-processed])');
672
+ if (pending.length === 0) return;
673
+ try {
674
+ window.mermaid.run({ nodes: pending });
675
+ } catch (e) {
676
+ console.warn('Mermaid render error:', e);
677
+ }
678
+ }
679
+
680
+ const markdownModule = {
681
+ escapeHtml,
682
+ mdToHtml,
683
+ squashOutsideCode,
684
+ renderContent,
685
+ processWithThinking,
686
+ createCollapsible,
687
+ hasUnclosedThinkTag,
688
+ extractThinkingBlocks,
689
+ startsWithReasoningPrefix,
690
+ renderMermaid
691
+ };
692
+
693
+ export default markdownModule;
694
+
695
+ // Mermaid is loaded async so it cannot delay the app shell.
696
+ function initMermaid() {
697
+ if (!window.mermaid || window.__zeroMermaidReady) return;
698
+ window.mermaid.initialize({ startOnLoad: false, theme: 'dark', securityLevel: 'loose' });
699
+ window.__zeroMermaidReady = true;
700
+ }
701
+ window.zeroInitMermaid = initMermaid;
702
+ initMermaid();
703
+
704
+ // Persist which thinking sections were expanded across page refreshes.
705
+ // IDs are render-generated (Date.now-based) so we key by a stable hash of
706
+ // the inner text content instead — same content reproduces the same hash on
707
+ // reload. LocalStorage holds a Set of expanded hashes; we observe the chat
708
+ // history and re-expand matching sections as they're inserted.
709
+ const THINK_EXPANDED_KEY = 'zero-thinking-expanded';
710
+ function _loadExpandedSet() {
711
+ try { return new Set(JSON.parse(localStorage.getItem(THINK_EXPANDED_KEY) || '[]')); }
712
+ catch { return new Set(); }
713
+ }
714
+ function _saveExpandedSet(set) {
715
+ try {
716
+ const arr = [...set];
717
+ // Bound storage growth — keep the most recent 200 entries.
718
+ if (arr.length > 200) arr.splice(0, arr.length - 200);
719
+ localStorage.setItem(THINK_EXPANDED_KEY, JSON.stringify(arr));
720
+ } catch {}
721
+ }
722
+ function _hashThinkingContent(el) {
723
+ if (!el) return '';
724
+ const text = (el.textContent || '').trim();
725
+ if (!text) return '';
726
+ let h = 0;
727
+ for (let i = 0; i < text.length; i++) {
728
+ h = (h * 31 + text.charCodeAt(i)) | 0;
729
+ }
730
+ return String(h);
731
+ }
732
+ function _setThinkingExpanded(content, toggle, header, expanded) {
733
+ if (!content || !toggle) return;
734
+ content.classList.toggle('expanded', expanded);
735
+ toggle.classList.toggle('expanded', expanded);
736
+ const label_el = header?.querySelector('.thinking-header-left span');
737
+ if (label_el) {
738
+ const label = label_el.dataset.label || 'thinking process';
739
+ label_el.textContent = expanded ? `Hide ${label}` : `View ${label}`;
740
+ }
741
+ }
742
+
743
+ // Delegated click handler for thinking toggle (CSP-safe, no inline onclick)
744
+ document.addEventListener('click', function(e) {
745
+ const header = e.target.closest('.thinking-header[data-thinking-id]');
746
+ if (!header) return;
747
+ const id = header.dataset.thinkingId;
748
+ const content = document.getElementById(id);
749
+ const toggle = document.getElementById(id + '-toggle');
750
+ if (!content || !toggle) return;
751
+
752
+ const willExpand = !content.classList.contains('expanded');
753
+ _setThinkingExpanded(content, toggle, header, willExpand);
754
+
755
+ // Persist by content hash so the choice survives a refresh.
756
+ const hash = _hashThinkingContent(content);
757
+ if (!hash) return;
758
+ const set = _loadExpandedSet();
759
+ if (willExpand) set.add(hash);
760
+ else set.delete(hash);
761
+ _saveExpandedSet(set);
762
+ });
763
+
764
+ // Watch the chat history; whenever a thinking section appears, expand it if
765
+ // its hash matches one the user previously expanded.
766
+ (function _watchThinking() {
767
+ if (window._thinkingWatcherWired) return;
768
+ window._thinkingWatcherWired = true;
769
+ const _apply = (root) => {
770
+ if (!root || !root.querySelectorAll) return;
771
+ const sections = root.matches?.('.thinking-section')
772
+ ? [root]
773
+ : [...root.querySelectorAll('.thinking-section')];
774
+ if (!sections.length) return;
775
+ const set = _loadExpandedSet();
776
+ if (!set.size) return;
777
+ for (const sec of sections) {
778
+ const content = sec.querySelector('.thinking-content');
779
+ if (!content) continue;
780
+ if (content.classList.contains('expanded')) continue;
781
+ const hash = _hashThinkingContent(content);
782
+ if (!hash || !set.has(hash)) continue;
783
+ const header = sec.querySelector('.thinking-header[data-thinking-id]');
784
+ const id = header?.dataset.thinkingId;
785
+ const toggle = id ? document.getElementById(id + '-toggle') : null;
786
+ _setThinkingExpanded(content, toggle, header, true);
787
+ }
788
+ };
789
+ const start = () => {
790
+ const root = document.body;
791
+ if (!root) return;
792
+ _apply(root);
793
+ new MutationObserver((mutations) => {
794
+ for (const m of mutations) {
795
+ for (const node of m.addedNodes) {
796
+ if (node.nodeType === 1) _apply(node);
797
+ }
798
+ }
799
+ }).observe(root, { childList: true, subtree: true });
800
+ };
801
+ if (document.readyState === 'loading') {
802
+ document.addEventListener('DOMContentLoaded', start, { once: true });
803
+ } else {
804
+ start();
805
+ }
806
+ })();