synabun 2.0.7 → 2.0.10

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 (565) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +1 -1
  3. package/THIRD-PARTY-LICENSES.md +46 -4
  4. package/hooks/claude-code/post-plan.mjs +6 -30
  5. package/hooks/claude-code/session-start.mjs +1 -4
  6. package/hooks/claude-code/shared.mjs +106 -72
  7. package/launcher.mjs +9 -3
  8. package/lib/package-processes.js +670 -0
  9. package/lib/runtime-dependencies.js +225 -0
  10. package/lib/start-launcher.js +6 -1
  11. package/mcp-server/dist/services/browser-bench.d.ts +1 -1
  12. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
  13. package/mcp-server/dist/services/browser-bench.js +1 -1
  14. package/mcp-server/dist/services/browser-bench.js.map +1 -1
  15. package/mcp-server/dist/services/categories.d.ts +35 -2
  16. package/mcp-server/dist/services/categories.d.ts.map +1 -1
  17. package/mcp-server/dist/services/categories.js +77 -33
  18. package/mcp-server/dist/services/categories.js.map +1 -1
  19. package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
  20. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
  21. package/mcp-server/dist/services/desktop-bench.js +1 -1
  22. package/mcp-server/dist/services/desktop-bench.js.map +1 -1
  23. package/mcp-server/dist/tools/agents.d.ts +1 -1
  24. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  25. package/mcp-server/dist/tools/agents.js +1 -1
  26. package/mcp-server/dist/tools/agents.js.map +1 -1
  27. package/mcp-server/dist/tools/bluesky.js +1 -1
  28. package/mcp-server/dist/tools/bluesky.js.map +1 -1
  29. package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
  30. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
  31. package/mcp-server/dist/tools/browser-advanced.js +1 -1
  32. package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
  33. package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
  34. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
  35. package/mcp-server/dist/tools/browser-batch.js +1 -1
  36. package/mcp-server/dist/tools/browser-batch.js.map +1 -1
  37. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
  38. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
  39. package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
  40. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
  41. package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
  42. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
  43. package/mcp-server/dist/tools/browser-interact.js +1 -1
  44. package/mcp-server/dist/tools/browser-interact.js.map +1 -1
  45. package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
  46. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
  47. package/mcp-server/dist/tools/browser-navigate.js +1 -1
  48. package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
  49. package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
  50. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
  51. package/mcp-server/dist/tools/browser-observe.js +1 -1
  52. package/mcp-server/dist/tools/browser-observe.js.map +1 -1
  53. package/mcp-server/dist/tools/card-tools.d.ts +1 -1
  54. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
  55. package/mcp-server/dist/tools/card-tools.js +1 -1
  56. package/mcp-server/dist/tools/card-tools.js.map +1 -1
  57. package/mcp-server/dist/tools/category.d.ts +1 -1
  58. package/mcp-server/dist/tools/category.d.ts.map +1 -1
  59. package/mcp-server/dist/tools/category.js +5 -1
  60. package/mcp-server/dist/tools/category.js.map +1 -1
  61. package/mcp-server/dist/tools/choice.d.ts +1 -1
  62. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  63. package/mcp-server/dist/tools/choice.js +1 -1
  64. package/mcp-server/dist/tools/choice.js.map +1 -1
  65. package/mcp-server/dist/tools/computer.d.ts +1 -1
  66. package/mcp-server/dist/tools/computer.d.ts.map +1 -1
  67. package/mcp-server/dist/tools/computer.js +1 -1
  68. package/mcp-server/dist/tools/computer.js.map +1 -1
  69. package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
  70. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
  71. package/mcp-server/dist/tools/discord-channel.js +1 -1
  72. package/mcp-server/dist/tools/discord-channel.js.map +1 -1
  73. package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
  74. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
  75. package/mcp-server/dist/tools/discord-guild.js +1 -1
  76. package/mcp-server/dist/tools/discord-guild.js.map +1 -1
  77. package/mcp-server/dist/tools/discord-member.d.ts +1 -1
  78. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
  79. package/mcp-server/dist/tools/discord-member.js +1 -1
  80. package/mcp-server/dist/tools/discord-member.js.map +1 -1
  81. package/mcp-server/dist/tools/discord-message.d.ts +1 -1
  82. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
  83. package/mcp-server/dist/tools/discord-message.js +1 -1
  84. package/mcp-server/dist/tools/discord-message.js.map +1 -1
  85. package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
  86. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
  87. package/mcp-server/dist/tools/discord-onboarding.js +1 -1
  88. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
  89. package/mcp-server/dist/tools/discord-role.d.ts +1 -1
  90. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/discord-role.js +1 -1
  92. package/mcp-server/dist/tools/discord-role.js.map +1 -1
  93. package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
  94. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
  95. package/mcp-server/dist/tools/discord-thread.js +1 -1
  96. package/mcp-server/dist/tools/discord-thread.js.map +1 -1
  97. package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
  98. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
  99. package/mcp-server/dist/tools/discord-webhook.js +1 -1
  100. package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
  101. package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
  102. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
  103. package/mcp-server/dist/tools/fb-groups.js +1 -1
  104. package/mcp-server/dist/tools/fb-groups.js.map +1 -1
  105. package/mcp-server/dist/tools/forget.d.ts +1 -1
  106. package/mcp-server/dist/tools/forget.d.ts.map +1 -1
  107. package/mcp-server/dist/tools/forget.js +1 -1
  108. package/mcp-server/dist/tools/forget.js.map +1 -1
  109. package/mcp-server/dist/tools/git-tools.d.ts +1 -1
  110. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
  111. package/mcp-server/dist/tools/git-tools.js +1 -1
  112. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  113. package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
  114. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
  115. package/mcp-server/dist/tools/gsc/experience.js +1 -1
  116. package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
  117. package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
  118. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
  119. package/mcp-server/dist/tools/gsc/indexing.js +1 -1
  120. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
  121. package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
  122. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
  123. package/mcp-server/dist/tools/gsc/inspect.js +1 -1
  124. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
  125. package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
  126. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
  127. package/mcp-server/dist/tools/gsc/navigate.js +1 -1
  128. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
  129. package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
  130. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
  131. package/mcp-server/dist/tools/gsc/performance.js +1 -1
  132. package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
  133. package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
  134. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
  135. package/mcp-server/dist/tools/gsc/settings.js +1 -1
  136. package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
  137. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  138. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
  139. package/mcp-server/dist/tools/image-tools.js +1 -1
  140. package/mcp-server/dist/tools/image-tools.js.map +1 -1
  141. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
  142. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
  143. package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
  144. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
  145. package/mcp-server/dist/tools/loop.d.ts +1 -1
  146. package/mcp-server/dist/tools/loop.d.ts.map +1 -1
  147. package/mcp-server/dist/tools/loop.js +1 -1
  148. package/mcp-server/dist/tools/loop.js.map +1 -1
  149. package/mcp-server/dist/tools/memories.d.ts +1 -1
  150. package/mcp-server/dist/tools/memories.d.ts.map +1 -1
  151. package/mcp-server/dist/tools/memories.js +1 -1
  152. package/mcp-server/dist/tools/memories.js.map +1 -1
  153. package/mcp-server/dist/tools/morelogin.d.ts +1 -1
  154. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  155. package/mcp-server/dist/tools/morelogin.js +1 -1
  156. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  157. package/mcp-server/dist/tools/profile.d.ts +1 -1
  158. package/mcp-server/dist/tools/profile.d.ts.map +1 -1
  159. package/mcp-server/dist/tools/profile.js +1 -1
  160. package/mcp-server/dist/tools/profile.js.map +1 -1
  161. package/mcp-server/dist/tools/recall.d.ts +1 -1
  162. package/mcp-server/dist/tools/recall.d.ts.map +1 -1
  163. package/mcp-server/dist/tools/recall.js +1 -1
  164. package/mcp-server/dist/tools/recall.js.map +1 -1
  165. package/mcp-server/dist/tools/reflect.d.ts +1 -1
  166. package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
  167. package/mcp-server/dist/tools/reflect.js +1 -1
  168. package/mcp-server/dist/tools/reflect.js.map +1 -1
  169. package/mcp-server/dist/tools/remember.d.ts +1 -1
  170. package/mcp-server/dist/tools/remember.d.ts.map +1 -1
  171. package/mcp-server/dist/tools/remember.js +13 -11
  172. package/mcp-server/dist/tools/remember.js.map +1 -1
  173. package/mcp-server/dist/tools/restore.d.ts +1 -1
  174. package/mcp-server/dist/tools/restore.d.ts.map +1 -1
  175. package/mcp-server/dist/tools/restore.js +1 -1
  176. package/mcp-server/dist/tools/restore.js.map +1 -1
  177. package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
  178. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
  179. package/mcp-server/dist/tools/style-guide-tools.js +1 -1
  180. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
  181. package/mcp-server/dist/tools/sync.d.ts +1 -1
  182. package/mcp-server/dist/tools/sync.d.ts.map +1 -1
  183. package/mcp-server/dist/tools/sync.js +1 -1
  184. package/mcp-server/dist/tools/sync.js.map +1 -1
  185. package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
  186. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
  187. package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
  188. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
  189. package/mcp-server/dist/tools/utils.d.ts +1 -1
  190. package/mcp-server/dist/tools/utils.d.ts.map +1 -1
  191. package/mcp-server/dist/tools/utils.js +1 -1
  192. package/mcp-server/dist/tools/utils.js.map +1 -1
  193. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +1 -1
  194. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -1
  195. package/mcp-server/dist/tools/whatsapp-send-file.js +1 -1
  196. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -1
  197. package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
  198. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  199. package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
  200. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  201. package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
  202. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
  203. package/mcp-server/dist/tools/youtube/analytics.js +1 -1
  204. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
  205. package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
  206. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
  207. package/mcp-server/dist/tools/youtube/download.js +1 -1
  208. package/mcp-server/dist/tools/youtube/download.js.map +1 -1
  209. package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
  210. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
  211. package/mcp-server/dist/tools/youtube/media.js +1 -1
  212. package/mcp-server/dist/tools/youtube/media.js.map +1 -1
  213. package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
  214. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
  215. package/mcp-server/dist/tools/youtube/metadata.js +1 -1
  216. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
  217. package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
  218. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
  219. package/mcp-server/dist/tools/youtube/navigate.js +1 -1
  220. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
  221. package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
  222. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
  223. package/mcp-server/dist/tools/youtube/playlist.js +1 -1
  224. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
  225. package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
  226. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
  227. package/mcp-server/dist/tools/youtube/schedule.js +1 -1
  228. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
  229. package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
  230. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
  231. package/mcp-server/dist/tools/youtube/source.js +1 -1
  232. package/mcp-server/dist/tools/youtube/source.js.map +1 -1
  233. package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
  234. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
  235. package/mcp-server/dist/tools/youtube/upload.js +4 -9
  236. package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
  237. package/mcp-server/dist/tui.js +16 -4
  238. package/mcp-server/dist/tui.js.map +1 -1
  239. package/mcp-server/package-lock.json +3 -3
  240. package/mcp-server/package.json +3 -3
  241. package/mcp-server/src/services/browser-bench.ts +1 -1
  242. package/mcp-server/src/services/categories.ts +77 -32
  243. package/mcp-server/src/services/desktop-bench.ts +1 -1
  244. package/mcp-server/src/tools/agents.ts +1 -1
  245. package/mcp-server/src/tools/bluesky.ts +1 -1
  246. package/mcp-server/src/tools/browser-advanced.ts +1 -1
  247. package/mcp-server/src/tools/browser-batch.ts +1 -1
  248. package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
  249. package/mcp-server/src/tools/browser-interact.ts +1 -1
  250. package/mcp-server/src/tools/browser-navigate.ts +1 -1
  251. package/mcp-server/src/tools/browser-observe.ts +1 -1
  252. package/mcp-server/src/tools/card-tools.ts +1 -1
  253. package/mcp-server/src/tools/category.ts +6 -1
  254. package/mcp-server/src/tools/choice.ts +1 -1
  255. package/mcp-server/src/tools/computer.ts +1 -1
  256. package/mcp-server/src/tools/discord-channel.ts +1 -1
  257. package/mcp-server/src/tools/discord-guild.ts +1 -1
  258. package/mcp-server/src/tools/discord-member.ts +1 -1
  259. package/mcp-server/src/tools/discord-message.ts +1 -1
  260. package/mcp-server/src/tools/discord-onboarding.ts +1 -1
  261. package/mcp-server/src/tools/discord-role.ts +1 -1
  262. package/mcp-server/src/tools/discord-thread.ts +1 -1
  263. package/mcp-server/src/tools/discord-webhook.ts +1 -1
  264. package/mcp-server/src/tools/fb-groups.ts +1 -1
  265. package/mcp-server/src/tools/forget.ts +1 -1
  266. package/mcp-server/src/tools/git-tools.ts +1 -1
  267. package/mcp-server/src/tools/gsc/experience.ts +1 -1
  268. package/mcp-server/src/tools/gsc/indexing.ts +1 -1
  269. package/mcp-server/src/tools/gsc/inspect.ts +1 -1
  270. package/mcp-server/src/tools/gsc/navigate.ts +1 -1
  271. package/mcp-server/src/tools/gsc/performance.ts +1 -1
  272. package/mcp-server/src/tools/gsc/settings.ts +1 -1
  273. package/mcp-server/src/tools/image-tools.ts +1 -1
  274. package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
  275. package/mcp-server/src/tools/loop.ts +1 -1
  276. package/mcp-server/src/tools/memories.ts +1 -1
  277. package/mcp-server/src/tools/morelogin.ts +1 -1
  278. package/mcp-server/src/tools/profile.ts +1 -1
  279. package/mcp-server/src/tools/recall.ts +1 -1
  280. package/mcp-server/src/tools/reflect.ts +1 -1
  281. package/mcp-server/src/tools/remember.ts +13 -12
  282. package/mcp-server/src/tools/restore.ts +1 -1
  283. package/mcp-server/src/tools/style-guide-tools.ts +1 -1
  284. package/mcp-server/src/tools/sync.ts +1 -1
  285. package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
  286. package/mcp-server/src/tools/utils.ts +1 -1
  287. package/mcp-server/src/tools/whatsapp-send-file.ts +1 -1
  288. package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
  289. package/mcp-server/src/tools/youtube/analytics.ts +1 -1
  290. package/mcp-server/src/tools/youtube/download.ts +1 -1
  291. package/mcp-server/src/tools/youtube/media.ts +1 -1
  292. package/mcp-server/src/tools/youtube/metadata.ts +1 -1
  293. package/mcp-server/src/tools/youtube/navigate.ts +1 -1
  294. package/mcp-server/src/tools/youtube/playlist.ts +1 -1
  295. package/mcp-server/src/tools/youtube/schedule.ts +1 -1
  296. package/mcp-server/src/tools/youtube/source.ts +1 -1
  297. package/mcp-server/src/tools/youtube/upload.ts +4 -12
  298. package/mcp-server/src/tui.ts +22 -9
  299. package/neural-interface/i18n/en.json +236 -7
  300. package/neural-interface/i18n/pt-BR.json +238 -9
  301. package/neural-interface/lib/account-usage.js +5 -3
  302. package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
  303. package/neural-interface/lib/agent-sandbox-setting.js +57 -0
  304. package/neural-interface/lib/app-controls-api.js +161 -0
  305. package/neural-interface/lib/assistant-api.js +40 -7
  306. package/neural-interface/lib/assistant-brains/opencode.js +13 -1
  307. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
  308. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
  309. package/neural-interface/lib/assistant-catalog.js +17 -2
  310. package/neural-interface/lib/assistant-dispatch.js +3 -0
  311. package/neural-interface/lib/assistant-envelope.js +19 -1
  312. package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
  313. package/neural-interface/lib/assistant-runtime.js +131 -8
  314. package/neural-interface/lib/claude-model-catalog.js +64 -14
  315. package/neural-interface/lib/codex-account-auth.js +4 -2
  316. package/neural-interface/lib/codex-accounts.js +14 -1
  317. package/neural-interface/lib/codex-settings-schema.js +111 -47
  318. package/neural-interface/lib/editor-completion-opencode.js +132 -35
  319. package/neural-interface/lib/exec-loop-command.js +11 -2
  320. package/neural-interface/lib/install-details.js +23 -5
  321. package/neural-interface/lib/launch-cache.js +293 -0
  322. package/neural-interface/lib/login-start.js +524 -0
  323. package/neural-interface/lib/native-loop-providers.js +24 -4
  324. package/neural-interface/lib/native-loop-runtime.js +19 -0
  325. package/neural-interface/lib/opencode-db-schema.js +75 -0
  326. package/neural-interface/lib/opencode-history.js +16 -1
  327. package/neural-interface/lib/opencode-protocol.js +173 -0
  328. package/neural-interface/lib/opencode-provider-view.js +81 -0
  329. package/neural-interface/lib/opencode-runtime-config.js +10 -3
  330. package/neural-interface/lib/opencode-serve-records.js +606 -0
  331. package/neural-interface/lib/opencode-v2-client.js +33 -6
  332. package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
  333. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  334. package/neural-interface/lib/opencode2-client.js +760 -0
  335. package/neural-interface/lib/opencode2-proxy.js +116 -0
  336. package/neural-interface/lib/opencode2-translate.js +1008 -0
  337. package/neural-interface/lib/process-identity.js +361 -0
  338. package/neural-interface/lib/provider-panel-terminal-auth.js +3 -2
  339. package/neural-interface/lib/session-cache-builder.js +26 -5
  340. package/neural-interface/lib/session-indexer.js +3 -1
  341. package/neural-interface/lib/session-title-generator.js +20 -0
  342. package/neural-interface/lib/style-guide/api.js +4 -3
  343. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  344. package/neural-interface/package-lock.json +1342 -44
  345. package/neural-interface/package.json +5 -1
  346. package/neural-interface/public/index.html +2 -2
  347. package/neural-interface/public/shared/account-menu.js +4 -1
  348. package/neural-interface/public/shared/account-usage.js +62 -2
  349. package/neural-interface/public/shared/account-view.js +2 -1
  350. package/neural-interface/public/shared/agent-runtime-options.js +55 -11
  351. package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
  352. package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
  353. package/neural-interface/public/shared/assistant/asst-api.js +18 -3
  354. package/neural-interface/public/shared/assistant/asst-brain-picker.js +424 -152
  355. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  356. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  357. package/neural-interface/public/shared/assistant/asst-menu.js +275 -77
  358. package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
  359. package/neural-interface/public/shared/assistant/asst-panel.js +236 -35
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
  361. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  362. package/neural-interface/public/shared/assistant/asst-render.js +382 -36
  363. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  364. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  365. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  366. package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
  367. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  368. package/neural-interface/public/shared/assistant/asst-state.js +14 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +109 -17
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
  371. package/neural-interface/public/shared/cc-dropdown.js +480 -0
  372. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  373. package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
  374. package/neural-interface/public/shared/cdx/cdx-styles.js +49 -5
  375. package/neural-interface/public/shared/cdx/cdx-tabs.js +152 -35
  376. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  377. package/neural-interface/public/shared/data-cache.js +223 -0
  378. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  379. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  380. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  381. package/neural-interface/public/shared/desktop-update-view.js +15 -5
  382. package/neural-interface/public/shared/file-links.js +73 -8
  383. package/neural-interface/public/shared/html-shell.js +74 -80
  384. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  385. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  386. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +11 -3
  387. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  388. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +97 -3
  389. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
  390. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  391. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  392. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
  393. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  394. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
  395. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
  396. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +83 -5
  397. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
  398. package/neural-interface/public/shared/opencode-providers.js +39 -0
  399. package/neural-interface/public/shared/schedules-view.js +318 -0
  400. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  401. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  402. package/neural-interface/public/shared/settings/settings-data.js +88 -0
  403. package/neural-interface/public/shared/settings/settings-ia.js +17 -0
  404. package/neural-interface/public/shared/showcase-privacy.js +2 -1
  405. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  406. package/neural-interface/public/shared/styles.css +543 -372
  407. package/neural-interface/public/shared/ui-assistant.js +27 -5
  408. package/neural-interface/public/shared/ui-automation-studio.js +67 -27
  409. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  410. package/neural-interface/public/shared/ui-claude-panel.js +126 -12
  411. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  412. package/neural-interface/public/shared/ui-detail.js +6 -6
  413. package/neural-interface/public/shared/ui-explorer.js +2 -0
  414. package/neural-interface/public/shared/ui-file-links.js +1 -0
  415. package/neural-interface/public/shared/ui-image-gallery.js +6 -5
  416. package/neural-interface/public/shared/ui-invite.js +18 -2
  417. package/neural-interface/public/shared/ui-link.js +9 -9
  418. package/neural-interface/public/shared/ui-menubar.js +232 -33
  419. package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
  420. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  421. package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
  422. package/neural-interface/public/shared/ui-sessions.js +15 -4
  423. package/neural-interface/public/shared/ui-settings.js +87 -36
  424. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  425. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  426. package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
  427. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  428. package/neural-interface/public/shared/ui-skills.js +70 -12
  429. package/neural-interface/public/shared/ui-sync.js +9 -0
  430. package/neural-interface/public/shared/ui-terminal.js +36 -20
  431. package/neural-interface/public/shared/ui-trash.js +8 -1
  432. package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
  433. package/neural-interface/public/shared/ui-tutorial.js +15 -5
  434. package/neural-interface/public/shared/ui-update.js +87 -16
  435. package/neural-interface/public/shared/ui-whiteboard.js +2 -1
  436. package/neural-interface/public/variant/2d/main.js +4 -1
  437. package/neural-interface/public/variant/3d/main.js +3 -0
  438. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  439. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  440. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  441. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  442. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  443. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  444. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  445. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  446. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  447. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  448. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  449. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  450. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  451. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  452. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  453. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  454. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  455. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  456. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  457. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  458. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  459. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  460. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  461. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  462. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  463. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  464. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  465. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  466. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  467. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  468. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  469. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  470. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  471. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  472. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  473. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  474. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  475. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  476. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  477. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  478. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  479. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  480. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  481. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  482. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  483. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  484. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  485. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  486. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  487. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  488. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  489. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  490. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  491. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  492. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  493. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  494. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  495. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  496. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  497. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  498. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  499. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  500. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  501. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  502. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  503. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  504. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  505. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  506. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  507. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  508. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  509. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  510. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  511. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  512. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  513. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  514. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  515. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  516. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  517. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  518. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  519. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  520. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  521. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  522. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  523. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  524. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  525. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  526. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  527. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  528. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  529. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  530. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  531. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  532. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  533. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  534. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  535. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  536. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  537. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  538. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  539. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  540. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  541. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  542. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  543. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  544. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  545. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  546. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  547. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  548. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  549. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  550. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  551. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  552. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  553. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  554. package/neural-interface/scripts/build-vendor.mjs +84 -3
  555. package/neural-interface/scripts/rebuild-pty.js +14 -2
  556. package/neural-interface/server.js +1279 -372
  557. package/npm-shrinkwrap.json +4424 -0
  558. package/package.json +54 -2
  559. package/postinstall.js +61 -47
  560. package/preuninstall.js +8 -35
  561. package/setup.js +76 -44
  562. package/skills/synabun/modules/idea.md +1 -1
  563. package/skills/synabun/modules/schedule.md +2 -2
  564. package/updater.cmd +2 -2
  565. package/updater.mjs +97 -3
@@ -204,7 +204,7 @@ export function createComposer(hostEl, hooks = {}) {
204
204
  function renderAttachments() {
205
205
  const chips = [];
206
206
  state.images.forEach((img, i) => {
207
- chips.push(`<span class="asst-chip static" data-kind="image" data-idx="${i}"><img src="data:${esc(img.mediaType)};base64,${img.base64}" alt=""><span class="asst-chip-text">${esc(img.name || 'image')}</span><button type="button" class="asst-chip-x" aria-label="${esc(t('common.remove', 'Remove'))}">${ICON_X}</button></span>`);
207
+ chips.push(`<span class="asst-chip static" data-kind="image" data-idx="${i}"><img src="data:${esc(img.mediaType)};base64,${esc(img.base64)}" alt=""><span class="asst-chip-text">${esc(img.name || 'image')}</span><button type="button" class="asst-chip-x" aria-label="${esc(t('common.remove', 'Remove'))}">${ICON_X}</button></span>`);
208
208
  });
209
209
  state.files.forEach((f, i) => {
210
210
  chips.push(`<span class="asst-chip static" data-kind="file" data-idx="${i}"><span class="asst-chip-text">${esc(f.name)}</span><span class="asst-chip-meta">${esc(Math.ceil(f.content.length / 1024))}k</span><button type="button" class="asst-chip-x" aria-label="${esc(t('common.remove', 'Remove'))}">${ICON_X}</button></span>`);
@@ -54,7 +54,7 @@ export function describeBrainFallback(fallback) {
54
54
  }
55
55
 
56
56
  /**
57
- * deps: { api: { list(), get(id), create({brain,label,fallback}) },
57
+ * deps: { api: { list(), get(id), create({brain,label,fallback,draft}), remove?(id) },
58
58
  * isBlocked() → bool, toast(text), storedBrain() → brain|null,
59
59
  * fallbackText(session.fallback) → text, defaultHost: 'terminal' }
60
60
  */
@@ -149,7 +149,9 @@ export function createAssistantHosts({
149
149
  try {
150
150
  // A model disabled since it was picked (Assistant → Models) must not
151
151
  // lock the Assistant out: the server starts on its enabled stand-in.
152
- res = await api.create({ brain: b, label: label || undefined, fallback: true });
152
+ // A draft: the server stores and lists it from its first prompt on (a prompt
153
+ // given here is that prompt); closed or left unused, nothing of it remains.
154
+ res = await api.create({ brain: b, label: label || undefined, fallback: true, draft: true });
153
155
  } catch (err) {
154
156
  say(`Assistant unavailable: ${err?.message || err}`);
155
157
  return null;
@@ -245,6 +247,48 @@ export function createAssistantHosts({
245
247
  return true;
246
248
  }
247
249
 
250
+ /**
251
+ * A session nobody chatted in that the user left (they picked another one in its Sessions menu):
252
+ * its tab closes the way the user closes one, and the server removes it (it holds no conversation).
253
+ */
254
+ function discard(sessionId) {
255
+ const owner = ownerOf(sessionId);
256
+ if (last?.sessionId === sessionId) last = null;
257
+ if (!owner) return false;
258
+ adapters.get(owner).close(sessionId, { closeServer: true });
259
+ return true;
260
+ }
261
+
262
+ /**
263
+ * The Sessions menu's Remove: the server stops the session's running turn and workers and purges it,
264
+ * then its tab closes wherever it is open (other windows: the session-ended broadcast). `fresh`: it is
265
+ * the session showing where the user asked, and the host that has it gets a new one first, as New does
266
+ * (a host is never left empty in between). → { ok: true, stoppedRuns } | { ok: false, error }
267
+ */
268
+ async function remove(sessionId, { host = null, fresh = false, brain = null } = {}) {
269
+ if (!sessionId || !api.remove) return { ok: false, error: 'unavailable' };
270
+ if (isBlocked()) return { ok: false, error: 'blocked' };
271
+ if (fresh) { try { await open({ host: host || ownerOf(sessionId), brain }); } catch { /* the removal goes on */ } }
272
+ let res = null;
273
+ try { res = await api.remove(sessionId); }
274
+ catch (err) { return { ok: false, error: err?.message || String(err) }; }
275
+ ended(sessionId);
276
+ return { ok: true, stoppedRuns: Array.isArray(res?.stoppedRuns) ? res.stoppedRuns : [] };
277
+ }
278
+
279
+ /**
280
+ * The server no longer has a session a host shows (a draft it dropped, a restart before its first
281
+ * prompt, a removal this page missed): its tab closes, and with `fresh` (it was showing and had
282
+ * no conversation) a new session takes its place in the host that had it, so the user never sees a dead tab.
283
+ */
284
+ async function gone(sessionId, { host = null, fresh = false, brain = null } = {}) {
285
+ if (!sessionId) return null;
286
+ let opened = null;
287
+ if (fresh && !isBlocked()) { try { opened = await open({ host: host || ownerOf(sessionId), brain }); } catch { opened = null; } }
288
+ ended(sessionId);
289
+ return opened;
290
+ }
291
+
248
292
  return {
249
293
  register,
250
294
  ownerOf,
@@ -253,6 +297,9 @@ export function createAssistantHosts({
253
297
  toggle,
254
298
  show,
255
299
  ended,
300
+ discard,
301
+ remove,
302
+ gone,
256
303
  noteFocus,
257
304
  fetchLive,
258
305
  isLive: isAssistantSessionLive,
@@ -4,14 +4,23 @@
4
4
  // One body-level popover for every assistant menu (brain fields, model menu,
5
5
  // sessions, "⋯", route mode). Appended to <body> so it escapes the viewport's
6
6
  // paint containment; positioned with fixed coordinates next to its anchor and
7
- // clamped to the window. Only one menu is open at a time.
7
+ // kept inside the Assistant that holds the anchor (the sidepanel, or a tab's own
8
+ // root) and the window. Only one menu is open at a time.
8
9
  //
9
10
  // openMenu(anchor, { title, items, placement:'auto'|'above'|'below'|'prefer-below', filter,
10
11
  // width, role:'menu'|'listbox', label, className, onClose,
11
- // zIndex (a menu opened from a layer above the panel's own, e.g. Settings) })
12
+ // zIndex (a menu opened from a layer above the panel's own, e.g. Settings),
13
+ // footer, bounds, onEscape })
12
14
  // 'prefer-below' opens under the anchor whenever a usable list fits there (the
13
15
  // menu scrolls inside that room), even when more room is above: a menu opened
14
16
  // from Settings must not cover the Settings header. The panel's menus use 'auto'.
17
+ // `footer`: items under the list that stay in view while the list scrolls.
18
+ // Every menu is measured at the width it is shown at, never wider than its box less the margins,
19
+ // and opens where that box has room (the list scrolls inside it). The box is the Assistant
20
+ // panel or root around the anchor, else the window.
21
+ // `bounds`: an element (or () => DOMRect) to use as that box instead: the menu is then exactly
22
+ // `width` wide (or the box's room) and follows the box on resize.
23
+ // `onEscape`: returns true when it handled Esc (e.g. went back a view): the menu stays open.
15
24
  // Item kinds:
16
25
  // item (default) { label, desc, icon, tag, disabled, danger, keywords, onSelect }
17
26
  // radio { ...item, selected } → menuitemradio / option
@@ -21,10 +30,19 @@
21
30
  // info { label, value } → non-interactive row
22
31
  // tag { label } → non-interactive badge row
23
32
  // input { placeholder, value, onSubmit } → inline text field
33
+ // buttons { label, buttons: [item…] } → one row of compact items (a group)
34
+ // `action: { icon, label, tooltip, id, onSelect }` on an item (or radio / check) adds one icon button at the
35
+ // row's end, shown on hover and keyboard focus (the Sessions menu's Remove). It is not a stop of ↑/↓: → from
36
+ // its row reaches it, ← goes back, Delete on the row presses it; choosing it never chooses the row and leaves
37
+ // the menu open (its onSelect usually redraws it with update()).
38
+ // `render(button, { checked })` on an item draws its content instead of label/desc/tag.
39
+ // `className` on an item or an info row adds its own class.
40
+ // `keepOpen` on an item leaves the menu open when it is chosen.
24
41
  // `restoreFocus` on an item or input sends focus back to the anchor once it is chosen.
25
42
  // Keys: ↑/↓ move, Home/End jump, Enter/Space activate, Esc closes (focus back
26
43
  // to the anchor), Tab closes. The optional filter hides non-matching items and
27
- // any group left empty.
44
+ // any group left empty. The handle's update({ title, items, footer, focus }) redraws
45
+ // an open menu in place (focus: the data-id to land on; the filter's text still applies).
28
46
 
29
47
  let _open = null;
30
48
  let _uid = 0;
@@ -87,11 +105,85 @@ export function currentMenu() {
87
105
  return _open?.menu || null;
88
106
  }
89
107
 
108
+ /** The element the open menu belongs to (null when none is open). */
109
+ export function currentMenuAnchor() {
110
+ return _open?.anchor || null;
111
+ }
112
+
113
+ function itemButton(item, kind, { role, list, onActivate }) {
114
+ const btn = el('button', `asst-dd-item${item.danger ? ' danger' : ''}${item.className ? ` ${item.className}` : ''}`);
115
+ btn.type = 'button';
116
+ btn.tabIndex = -1;
117
+ btn.dataset.kind = kind;
118
+ if (item.id != null) btn.dataset.id = String(item.id);
119
+ btn.dataset.search = `${item.label || ''} ${item.desc || ''} ${item.keywords || ''}`.toLowerCase();
120
+ const checked = kind === 'radio' ? !!item.selected : kind === 'check' ? !!item.checked : false;
121
+ if (role === 'listbox') {
122
+ btn.setAttribute('role', 'option');
123
+ btn.setAttribute('aria-selected', checked ? 'true' : 'false');
124
+ } else if (kind === 'radio') {
125
+ btn.setAttribute('role', 'menuitemradio');
126
+ btn.setAttribute('aria-checked', checked ? 'true' : 'false');
127
+ } else if (kind === 'check') {
128
+ btn.setAttribute('role', 'menuitemcheckbox');
129
+ btn.setAttribute('aria-checked', checked ? 'true' : 'false');
130
+ } else {
131
+ btn.setAttribute('role', role === 'listbox' ? 'option' : 'menuitem');
132
+ }
133
+ if (item.disabled) btn.disabled = true;
134
+ if (typeof item.render === 'function') {
135
+ item.render(btn, { checked });
136
+ } else {
137
+ const lead = kind === 'radio' || kind === 'check'
138
+ ? `<span class="asst-dd-check" aria-hidden="true">${checked ? ICON_CHECK : ''}</span>`
139
+ : '';
140
+ btn.innerHTML = `${lead}${item.icon ? `<span class="asst-icon" aria-hidden="true"${item.color ? ` style="color:${esc(item.color)}"` : ''}>${item.icon}</span>` : ''}<span class="asst-dd-item-main"><span class="asst-dd-item-label">${esc(item.label)}</span>${item.desc ? `<span class="asst-dd-item-desc">${esc(item.desc)}</span>` : ''}</span>${item.tag ? `<span class="asst-dd-item-tag">${esc(item.tag)}</span>` : ''}${item.shortcut ? `<kbd class="asst-dd-kbd">${esc(item.shortcut)}</kbd>` : ''}`;
141
+ }
142
+ if (item.title) btn.title = item.title;
143
+ if (item.ariaLabel) btn.setAttribute('aria-label', item.ariaLabel);
144
+ btn.addEventListener('click', (e) => { e.stopPropagation(); if (!btn.disabled) onActivate(item, btn); });
145
+ btn.addEventListener('mousemove', () => {
146
+ if (btn.classList.contains('focused') || btn.disabled) return;
147
+ const menu = list.closest('.asst-dd-menu');
148
+ menu?.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
149
+ btn.classList.add('focused');
150
+ // Hover never steals focus from the filter / inline input.
151
+ if (!document.activeElement?.classList?.contains('asst-dd-input')) { try { btn.focus({ preventScroll: true }); } catch { /* ignore */ } }
152
+ });
153
+ if (checked) btn.dataset.checked = '1';
154
+ return btn;
155
+ }
156
+
157
+ /** A row's own action (item.action): an icon button beside the row, outside the ↑/↓ cycle. */
158
+ function actionButton(action, item, { role }) {
159
+ const btn = el('button', `asst-iconbtn asst-dd-item-action${action.className ? ` ${action.className}` : ''}`);
160
+ btn.type = 'button';
161
+ btn.tabIndex = -1;
162
+ btn.setAttribute('role', role === 'listbox' ? 'option' : 'menuitem');
163
+ if (action.id != null) btn.dataset.id = String(action.id);
164
+ const label = String(action.label || '');
165
+ if (label) btn.setAttribute('aria-label', label);
166
+ if (action.tooltip || label) btn.dataset.tooltip = action.tooltip || label;
167
+ btn.innerHTML = action.icon || '';
168
+ btn.querySelector('svg')?.setAttribute('aria-hidden', 'true');
169
+ btn.addEventListener('click', (e) => { e.stopPropagation(); action.onSelect?.(item, btn); });
170
+ return btn;
171
+ }
172
+
90
173
  function buildItems(list, items, { role, onActivate }) {
91
- const itemRole = role === 'listbox' ? 'option' : 'menuitem';
92
174
  for (const item of items || []) {
93
175
  const kind = itemKind(item);
94
176
  if (kind === 'separator') { list.appendChild(el('div', 'asst-dd-sep')).setAttribute('role', 'separator'); continue; }
177
+ if (kind === 'buttons') {
178
+ const row = el('div', `asst-dd-btnrow${item.className ? ` ${item.className}` : ''}`);
179
+ row.setAttribute('role', 'group');
180
+ if (item.id != null) row.dataset.id = String(item.id);
181
+ if (item.ariaLabel || item.label) row.setAttribute('aria-label', item.ariaLabel || item.label);
182
+ if (item.label) row.appendChild(el('span', 'asst-dd-btnrow-label', item.label)).setAttribute('aria-hidden', 'true');
183
+ for (const button of item.buttons || []) row.appendChild(itemButton(button, itemKind(button), { role, list, onActivate }));
184
+ list.appendChild(row);
185
+ continue;
186
+ }
95
187
  if (kind === 'header') {
96
188
  const h = el('div', 'asst-dd-group');
97
189
  h.setAttribute('role', 'presentation');
@@ -100,7 +192,7 @@ function buildItems(list, items, { role, onActivate }) {
100
192
  continue;
101
193
  }
102
194
  if (kind === 'info' || kind === 'tag') {
103
- const row = el('div', kind === 'tag' ? 'asst-dd-tagrow' : 'asst-dd-info');
195
+ const row = el('div', `${kind === 'tag' ? 'asst-dd-tagrow' : 'asst-dd-info'}${item.className ? ` ${item.className}` : ''}`);
104
196
  row.setAttribute('role', 'presentation');
105
197
  row.innerHTML = kind === 'tag'
106
198
  ? `<span class="asst-dd-tag">${esc(item.label)}</span>`
@@ -121,42 +213,15 @@ function buildItems(list, items, { role, onActivate }) {
121
213
  list.appendChild(input);
122
214
  continue;
123
215
  }
124
- const btn = el('button', `asst-dd-item${item.danger ? ' danger' : ''}`);
125
- btn.type = 'button';
126
- btn.tabIndex = -1;
127
- btn.dataset.kind = kind;
128
- if (item.id != null) btn.dataset.id = String(item.id);
129
- btn.dataset.search = `${item.label || ''} ${item.desc || ''} ${item.keywords || ''}`.toLowerCase();
130
- const checked = kind === 'radio' ? !!item.selected : kind === 'check' ? !!item.checked : false;
131
- if (role === 'listbox') {
132
- btn.setAttribute('role', 'option');
133
- btn.setAttribute('aria-selected', checked ? 'true' : 'false');
134
- } else if (kind === 'radio') {
135
- btn.setAttribute('role', 'menuitemradio');
136
- btn.setAttribute('aria-checked', checked ? 'true' : 'false');
137
- } else if (kind === 'check') {
138
- btn.setAttribute('role', 'menuitemcheckbox');
139
- btn.setAttribute('aria-checked', checked ? 'true' : 'false');
140
- } else {
141
- btn.setAttribute('role', itemRole);
216
+ if (item.action && typeof item.action === 'object' && !item.disabled) {
217
+ const row = el('div', 'asst-dd-itemrow');
218
+ row.setAttribute('role', 'none');
219
+ row.appendChild(itemButton(item, kind, { role, list, onActivate }));
220
+ row.appendChild(actionButton(item.action, item, { role }));
221
+ list.appendChild(row);
222
+ continue;
142
223
  }
143
- if (item.disabled) btn.disabled = true;
144
- const lead = kind === 'radio' || kind === 'check'
145
- ? `<span class="asst-dd-check" aria-hidden="true">${checked ? ICON_CHECK : ''}</span>`
146
- : '';
147
- btn.innerHTML = `${lead}${item.icon ? `<span class="asst-icon" aria-hidden="true"${item.color ? ` style="color:${esc(item.color)}"` : ''}>${item.icon}</span>` : ''}<span class="asst-dd-item-main"><span class="asst-dd-item-label">${esc(item.label)}</span>${item.desc ? `<span class="asst-dd-item-desc">${esc(item.desc)}</span>` : ''}</span>${item.tag ? `<span class="asst-dd-item-tag">${esc(item.tag)}</span>` : ''}${item.shortcut ? `<kbd class="asst-dd-kbd">${esc(item.shortcut)}</kbd>` : ''}`;
148
- if (item.title) btn.title = item.title;
149
- btn.addEventListener('click', (e) => { e.stopPropagation(); if (!btn.disabled) onActivate(item, btn); });
150
- btn.addEventListener('mousemove', () => {
151
- if (btn.classList.contains('focused') || btn.disabled) return;
152
- const menu = list.closest('.asst-dd-menu');
153
- menu?.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
154
- btn.classList.add('focused');
155
- // Hover never steals focus from the filter / inline input.
156
- if (!document.activeElement?.classList?.contains('asst-dd-input')) { try { btn.focus({ preventScroll: true }); } catch { /* ignore */ } }
157
- });
158
- if (checked) btn.dataset.checked = '1';
159
- list.appendChild(btn);
224
+ list.appendChild(itemButton(item, kind, { role, list, onActivate }));
160
225
  }
161
226
  if (!list.querySelector('.asst-dd-item, .asst-dd-input, .asst-dd-info, .asst-dd-tagrow')) list.appendChild(el('div', 'asst-dd-empty', '—'));
162
227
  }
@@ -167,6 +232,10 @@ export function applyMenuFilter(list, query, emptyLabel = 'No matches') {
167
232
  const rows = [...list.children];
168
233
  for (const row of rows) {
169
234
  if (row.classList.contains('asst-dd-item')) row.hidden = !!q && !row.dataset.search.includes(q);
235
+ // A row with its own action: shown while its item matches.
236
+ else if (row.classList.contains('asst-dd-itemrow')) row.hidden = !!q && !(row.querySelector('.asst-dd-item')?.dataset.search || '').includes(q);
237
+ // A button row stays whole: shown while one of its buttons matches.
238
+ else if (row.classList.contains('asst-dd-btnrow')) row.hidden = !!q && ![...row.querySelectorAll('.asst-dd-item')].some(b => b.dataset.search.includes(q));
170
239
  else if (row.classList.contains('asst-dd-info') || row.classList.contains('asst-dd-tagrow')) row.hidden = !!q;
171
240
  }
172
241
  // Group headers: visible only when an item until the next header/separator is visible.
@@ -192,7 +261,7 @@ export function applyMenuFilter(list, query, emptyLabel = 'No matches') {
192
261
  const last = visible[visible.length - 1];
193
262
  if (last?.classList.contains('asst-dd-sep')) last.hidden = true;
194
263
  let empty = list.querySelector('.asst-dd-empty.asst-dd-noresults');
195
- const hasItem = rows.some(r => !r.hidden && (r.classList.contains('asst-dd-item') || r.classList.contains('asst-dd-input')));
264
+ const hasItem = rows.some(r => !r.hidden && (r.classList.contains('asst-dd-item') || r.classList.contains('asst-dd-itemrow') || r.classList.contains('asst-dd-btnrow') || r.classList.contains('asst-dd-input')));
196
265
  if (q && !hasItem) {
197
266
  if (!empty) { empty = el('div', 'asst-dd-empty asst-dd-noresults', emptyLabel); list.appendChild(empty); }
198
267
  empty.hidden = false;
@@ -204,7 +273,8 @@ const PREFER_BELOW_MIN = 220;
204
273
 
205
274
  /**
206
275
  * Whether a menu opens above its anchor (pure; sizes in px). `above` / `below`:
207
- * the room on each side of the anchor.
276
+ * the room on each side of the anchor; `anchorTop` / `viewportHeight`: the anchor's
277
+ * top in the box the menu stays inside, and that box's height.
208
278
  */
209
279
  export function menuOpensAbove(placement, { anchorTop, anchorHeight, menuHeight, above, below, viewportHeight }) {
210
280
  if (placement === 'above') return menuHeight <= above || above >= below;
@@ -214,37 +284,101 @@ export function menuOpensAbove(placement, { anchorTop, anchorHeight, menuHeight,
214
284
  return !(menuHeight <= below || below >= above);
215
285
  }
216
286
 
217
- function positionMenu(menu, anchor, placement) {
218
- const margin = 8;
219
- const gap = 6;
287
+ // A menu: never narrower than this while the window allows it; never shorter than these
288
+ // (a bounded menu holds a title and a footer around its list).
289
+ const BOUNDED_MIN_WIDTH = 248;
290
+ const MENU_MIN_HEIGHT = 96;
291
+ const BOUNDED_MIN_HEIGHT = 140;
292
+
293
+ /** The box a menu stays inside: its bounds clipped to the window (the window when it has none). */
294
+ function boundsBox(bounds) {
220
295
  const vw = window.innerWidth;
221
296
  const vh = window.innerHeight;
222
- const r = anchor.getBoundingClientRect();
223
- menu.style.maxHeight = '';
297
+ let r = null;
298
+ try { r = typeof bounds === 'function' ? bounds() : bounds?.getBoundingClientRect?.(); } catch { r = null; }
299
+ if (!r || !(r.width > 0) || !(r.height > 0)) return { left: 0, top: 0, right: vw, bottom: vh };
300
+ return { left: Math.max(0, r.left), top: Math.max(0, r.top), right: Math.min(vw, r.right), bottom: Math.min(vh, r.bottom) };
301
+ }
302
+
303
+ /** Where a menu without `bounds` stays: the Assistant around its anchor (the sidepanel, or a tab's own root). */
304
+ function assistantOf(anchor) {
305
+ return anchor.closest?.('.assistant-panel') || anchor.closest?.('.asst-root') || null;
306
+ }
307
+
308
+ /** The widest a menu may be in its box (pure; px): the box less the margins, never under the minimum the window allows. */
309
+ export function menuRoomWidth({ box, viewportWidth, margin = 8 }) {
310
+ return Math.round(Math.max(box.right - box.left - margin * 2, Math.min(BOUNDED_MIN_WIDTH, viewportWidth - margin * 2)));
311
+ }
312
+
313
+ /** A bounded menu's width (pure; px): `width` at most, its box less the margins, never under the minimum the window allows. */
314
+ export function boundedMenuWidth({ box, viewportWidth, width, margin = 8 }) {
315
+ return Math.round(Math.max(Math.min(width || 320, box.right - box.left - margin * 2), Math.min(BOUNDED_MIN_WIDTH, viewportWidth - margin * 2)));
316
+ }
317
+
318
+ /**
319
+ * Where a menu goes (pure; px; rects as getBoundingClientRect gives them): under the anchor or
320
+ * above it as `placement` decides from the room inside the box (menuOpensAbove), as tall as that
321
+ * room; right-aligned with `alignX` (default: the anchor's right edge), or starting at the anchor's
322
+ * left edge with `alignLeft`; always inside the box and the window.
323
+ * → { left, top, maxHeight (null: its own height fits), up }.
324
+ */
325
+ export function boundedMenuPlacement({ anchor, box, viewportWidth, viewportHeight, width, menuHeight, placement = 'below', alignX = null, alignLeft = false, minHeight = BOUNDED_MIN_HEIGHT, margin = 8, gap = 6 }) {
326
+ const below = box.bottom - margin - (anchor.bottom + gap);
327
+ const above = anchor.top - gap - (box.top + margin);
328
+ const up = menuOpensAbove(placement, { anchorTop: anchor.top - box.top, anchorHeight: anchor.bottom - anchor.top, menuHeight, above, below, viewportHeight: box.bottom - box.top });
329
+ const room = Math.max(minHeight, Math.floor(up ? above : below));
330
+ const height = Math.min(menuHeight, room);
331
+ let top = up ? anchor.top - gap - height : anchor.bottom + gap;
332
+ top = Math.max(margin, Math.min(top, viewportHeight - margin - height));
333
+ let left = alignLeft ? anchor.left : (alignX ?? anchor.right) - width;
334
+ left = Math.min(left, box.right - margin - width);
335
+ left = Math.max(left, box.left + margin);
336
+ left = Math.max(margin, Math.min(left, viewportWidth - margin - width));
337
+ return { left: Math.round(left), top: Math.round(top), maxHeight: menuHeight > room ? room : null, up };
338
+ }
339
+
340
+ /**
341
+ * Place a menu inside its box: its width is final before it is measured (the CSS caps read the
342
+ * box's room through --asst-dd-room; a bounded menu is set to its width), so what is shown is what
343
+ * was placed; then boundedMenuPlacement. The list scrolls inside, title and footer stay.
344
+ */
345
+ function positionMenu(menu, anchor, { placement, bounds, width }) {
346
+ const viewportWidth = window.innerWidth;
347
+ const viewportHeight = window.innerHeight;
348
+ const box = boundsBox(bounds || assistantOf(anchor));
349
+ if (bounds) {
350
+ menu.style.minWidth = '0';
351
+ menu.style.maxWidth = 'none';
352
+ menu.style.width = `${boundedMenuWidth({ box, viewportWidth, width })}px`;
353
+ menu.style.maxHeight = 'none';
354
+ } else {
355
+ menu.style.setProperty('--asst-dd-room', `${menuRoomWidth({ box, viewportWidth })}px`);
356
+ menu.style.maxHeight = '';
357
+ }
224
358
  menu.style.left = '0px';
225
359
  menu.style.top = '0px';
226
- const mw = Math.min(menu.offsetWidth, vw - margin * 2);
227
- let mh = menu.offsetHeight;
228
- const above = r.top - gap - margin;
229
- const below = vh - r.bottom - gap - margin;
230
- const up = menuOpensAbove(placement, { anchorTop: r.top, anchorHeight: r.height, menuHeight: mh, above, below, viewportHeight: vh });
231
- const room = Math.max(96, up ? above : below);
232
- if (mh > room) { menu.style.maxHeight = `${Math.floor(room)}px`; mh = Math.floor(room); }
233
- let top = up ? r.top - gap - mh : r.bottom + gap;
234
- top = Math.max(margin, Math.min(top, vh - margin - mh));
235
- let left = r.left;
236
- if (anchor.closest('.assistant-panel')) {
237
- // Sidepanel hugs the right screen edge: grow leftward from the click point
238
- // (or the anchor's right edge for keyboard opens).
239
- const x = _lastDown && anchor.contains(_lastDown.target) && performance.now() - _lastDown.at < 1500 ? _lastDown.x : r.right;
240
- left = x - mw;
360
+ // The sidepanel hugs the right screen edge: its menus grow leftward from the click point (or the
361
+ // anchor's right edge for keyboard opens), as a bounded menu does; elsewhere a menu starts at its anchor.
362
+ const inSidepanel = !!anchor.closest?.('.assistant-panel');
363
+ const clicked = inSidepanel && _lastDown && anchor.contains(_lastDown.target) && performance.now() - _lastDown.at < 1500;
364
+ const spot = boundedMenuPlacement({
365
+ anchor: anchor.getBoundingClientRect(), box, viewportWidth, viewportHeight,
366
+ width: menu.offsetWidth, menuHeight: menu.offsetHeight, placement,
367
+ alignX: clicked ? _lastDown.x : null, alignLeft: !bounds && !inSidepanel,
368
+ minHeight: bounds ? BOUNDED_MIN_HEIGHT : MENU_MIN_HEIGHT,
369
+ });
370
+ if (spot.maxHeight != null) menu.style.maxHeight = `${spot.maxHeight}px`;
371
+ menu.style.left = `${spot.left}px`;
372
+ menu.style.top = `${spot.top}px`;
373
+ menu.dataset.placement = spot.up ? 'above' : 'below';
374
+ }
375
+
376
+ /** A row its menu's width cuts short (one-line label or description) keeps its words in a tooltip. */
377
+ function titleCutRows(menu) {
378
+ for (const btn of menu.querySelectorAll('.asst-dd-item:not([title])')) {
379
+ const parts = [...btn.querySelectorAll('.asst-dd-item-label, .asst-dd-item-desc')];
380
+ if (parts.some(n => n.scrollWidth > n.clientWidth + 1)) btn.title = parts.map(n => n.textContent).filter(Boolean).join(' · ');
241
381
  }
242
- if (left + mw > vw - margin) left = vw - margin - mw;
243
- left = Math.max(margin, left);
244
- menu.style.left = `${Math.round(left)}px`;
245
- menu.style.top = `${Math.round(top)}px`;
246
- menu.style.maxWidth = `${vw - margin * 2}px`;
247
- menu.dataset.placement = up ? 'above' : 'below';
248
382
  }
249
383
 
250
384
  /**
@@ -262,8 +396,9 @@ export function openMenu(anchor, opts = {}) {
262
396
  menu.setAttribute('role', role);
263
397
  menu.id = `asst-menu-${++_uid}`;
264
398
  menu.tabIndex = -1;
399
+ let heading = null;
265
400
  if (title) {
266
- const heading = el('div', 'asst-dd-menu-title', title);
401
+ heading = el('div', 'asst-dd-menu-title', title);
267
402
  heading.id = `${menu.id}-title`;
268
403
  menu.setAttribute('aria-labelledby', heading.id);
269
404
  menu.appendChild(heading);
@@ -290,13 +425,25 @@ export function openMenu(anchor, opts = {}) {
290
425
  };
291
426
  if (opts.content) list.appendChild(opts.content);
292
427
  else buildItems(list, items, { role, onActivate: activate });
293
- if (width) menu.style.minWidth = opts.repositionOnResize ? `min(${width}px, calc(100vw - 16px))` : `${width}px`;
294
- if (width && opts.repositionOnResize) menu.style.width = `min(${width}px, calc(100vw - 16px))`;
428
+ // The footer stays under the list: only the list scrolls.
429
+ let foot = null;
430
+ const fillFooter = (footer) => {
431
+ if (!foot) { foot = el('div', 'asst-dd-foot'); menu.appendChild(foot); }
432
+ foot.replaceChildren();
433
+ if (Array.isArray(footer) && footer.length) buildItems(foot, footer, { role, onActivate: activate });
434
+ foot.hidden = !(Array.isArray(footer) && footer.length);
435
+ };
436
+ if (Array.isArray(opts.footer)) fillFooter(opts.footer);
437
+ // `width`: at least that wide (exactly, with repositionOnResize), never wider than the room positionMenu sets.
438
+ const capped = `min(${width}px, var(--asst-dd-room, calc(100vw - 16px)))`;
439
+ if (width) menu.style.minWidth = capped;
440
+ if (width && opts.repositionOnResize) menu.style.width = capped;
295
441
  document.body.appendChild(menu);
296
- positionMenu(menu, anchor, placement);
442
+ const place = () => { positionMenu(menu, anchor, { placement, bounds: opts.bounds || null, width }); titleCutRows(menu); };
443
+ place();
297
444
 
298
445
  if (filterInput) {
299
- filterInput.addEventListener('input', () => { applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel); positionMenu(menu, anchor, placement); });
446
+ filterInput.addEventListener('input', () => { applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel); place(); });
300
447
  }
301
448
 
302
449
  const onDocDown = (e) => {
@@ -310,8 +457,15 @@ export function openMenu(anchor, opts = {}) {
310
457
  const target = e.target;
311
458
  const inInput = target?.classList?.contains('asst-dd-input');
312
459
  const nodes = focusables(menu);
313
- const idx = nodes.indexOf(document.activeElement);
314
- if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); closeMenu({ restoreFocus: true }); return; }
460
+ // On a row's action (item.action) the keys move from its row.
461
+ const onAction = document.activeElement?.classList?.contains('asst-dd-item-action') && menu.contains(document.activeElement) ? document.activeElement : null;
462
+ const idx = nodes.indexOf(onAction ? onAction.previousElementSibling : document.activeElement);
463
+ if (e.key === 'Escape') {
464
+ e.preventDefault(); e.stopPropagation();
465
+ if (typeof opts.onEscape === 'function' && opts.onEscape() === true) return;
466
+ closeMenu({ restoreFocus: true });
467
+ return;
468
+ }
315
469
  if (role === 'dialog') {
316
470
  if (e.key === 'Tab') {
317
471
  const fields = [...menu.querySelectorAll('button, input')].filter(node => !node.disabled && !node.hidden);
@@ -330,6 +484,18 @@ export function openMenu(anchor, opts = {}) {
330
484
  setFocused(menu, nodes[next]);
331
485
  return;
332
486
  }
487
+ if (!inInput && (e.key === 'ArrowRight' || e.key === 'ArrowLeft' || e.key === 'Delete')) {
488
+ const row = onAction ? null : document.activeElement?.closest?.('.asst-dd-item');
489
+ const action = onAction || (row?.parentElement?.classList.contains('asst-dd-itemrow') ? row.nextElementSibling : null);
490
+ if (action?.classList.contains('asst-dd-item-action')) {
491
+ e.preventDefault(); e.stopPropagation();
492
+ const main = action.previousElementSibling;
493
+ if (e.key === 'ArrowLeft') { if (onAction) setFocused(menu, main); return; }
494
+ if (!onAction) { setFocused(menu, main); try { action.focus({ preventScroll: true }); } catch { action.focus(); } }
495
+ if (e.key === 'Delete') action.click();
496
+ return;
497
+ }
498
+ }
333
499
  if ((e.key === 'Home' || e.key === 'End') && !inInput) {
334
500
  e.preventDefault(); e.stopPropagation();
335
501
  if (nodes.length) setFocused(menu, nodes[e.key === 'Home' ? 0 : nodes.length - 1]);
@@ -347,6 +513,7 @@ export function openMenu(anchor, opts = {}) {
347
513
  return;
348
514
  }
349
515
  if ((e.key === 'Enter' || e.key === ' ') && !inInput) {
516
+ if (onAction) { e.preventDefault(); e.stopPropagation(); onAction.click(); return; }
350
517
  const current = document.activeElement?.closest?.('.asst-dd-item');
351
518
  if (current && menu.contains(current)) { e.preventDefault(); e.stopPropagation(); current.click(); }
352
519
  return;
@@ -354,7 +521,7 @@ export function openMenu(anchor, opts = {}) {
354
521
  // Keep single-letter global keybinds out of an open menu.
355
522
  e.stopPropagation();
356
523
  };
357
- const onResize = () => opts.repositionOnResize ? positionMenu(menu, anchor, placement) : closeMenu();
524
+ const onResize = () => (opts.repositionOnResize || opts.bounds ? place() : closeMenu());
358
525
  const onBlur = () => closeMenu();
359
526
  document.addEventListener('mousedown', onDocDown, true);
360
527
  const keyTarget = role === 'dialog' ? window : document;
@@ -381,7 +548,38 @@ export function openMenu(anchor, opts = {}) {
381
548
  } else {
382
549
  setFocused(menu, selected || first || null);
383
550
  }
384
- return { el: menu, list, close: closeMenu, reposition: () => positionMenu(menu, anchor, placement) };
551
+ // Redraw in place (another view of the same menu): the anchor, the listeners and the focus stay in it.
552
+ // `keepPlace` (fresher data for the same view, read in the background): the scroll position, the row the
553
+ // keyboard is on and the filter's text and focus are left where the user had them.
554
+ const update = (next = {}) => {
555
+ if (!_open || _open.menu !== menu) return;
556
+ const keep = next.keepPlace === true;
557
+ const scrollBefore = list.scrollTop;
558
+ const active = document.activeElement;
559
+ const typing = !!active && active.classList?.contains('asst-dd-input') && menu.contains(active);
560
+ const onRowId = !typing ? (menu.querySelector('.asst-dd-item.focused')?.dataset?.id ?? null) : null;
561
+ if (typeof next.title === 'string' && heading) heading.textContent = next.title;
562
+ if (Array.isArray(next.items)) {
563
+ list.replaceChildren();
564
+ buildItems(list, next.items, { role, onActivate: activate });
565
+ if (filterInput?.value) applyMenuFilter(list, filterInput.value, opts.filterEmptyLabel);
566
+ list.scrollTop = keep ? scrollBefore : 0;
567
+ }
568
+ if ('footer' in next) fillFooter(next.footer);
569
+ place();
570
+ if (keep && typing) {
571
+ // The input keeps the caret; the row highlight follows the selection as it did when it opened.
572
+ menu.querySelectorAll('.asst-dd-item.focused').forEach(n => n.classList.remove('focused'));
573
+ list.querySelector('.asst-dd-item[data-checked="1"]:not([disabled])')?.classList.add('focused');
574
+ return;
575
+ }
576
+ const keptRow = keep && onRowId != null ? [...menu.querySelectorAll('.asst-dd-item')].find(n => n.dataset.id === onRowId && !n.disabled) : null;
577
+ const wanted = keptRow || (next.focus != null ? [...menu.querySelectorAll('.asst-dd-item')].find(n => n.dataset.id === String(next.focus) && !n.disabled) : null);
578
+ if (keep && !keptRow && !menu.contains(document.activeElement)) return; // focus is elsewhere: not taken back
579
+ setFocused(menu, wanted || list.querySelector('.asst-dd-item[data-checked="1"]:not([disabled])') || focusables(menu)[0] || null);
580
+ if (keep) list.scrollTop = scrollBefore;
581
+ };
582
+ return { el: menu, list, close: closeMenu, reposition: place, update };
385
583
  }
386
584
 
387
585
  /** Toggle helper for anchor buttons: closes when this anchor's menu is open. */