synabun 2.0.9 → 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 (252) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/THIRD-PARTY-LICENSES.md +46 -4
  4. package/launcher.mjs +9 -3
  5. package/lib/start-launcher.js +6 -1
  6. package/mcp-server/package-lock.json +2 -2
  7. package/mcp-server/package.json +1 -1
  8. package/neural-interface/i18n/en.json +218 -7
  9. package/neural-interface/i18n/pt-BR.json +220 -9
  10. package/neural-interface/lib/account-usage.js +5 -3
  11. package/neural-interface/lib/app-controls-api.js +161 -0
  12. package/neural-interface/lib/assistant-api.js +21 -7
  13. package/neural-interface/lib/assistant-brains/opencode.js +13 -1
  14. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
  15. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
  16. package/neural-interface/lib/assistant-catalog.js +17 -2
  17. package/neural-interface/lib/assistant-dispatch.js +3 -0
  18. package/neural-interface/lib/assistant-envelope.js +19 -1
  19. package/neural-interface/lib/assistant-runtime.js +127 -8
  20. package/neural-interface/lib/claude-model-catalog.js +64 -14
  21. package/neural-interface/lib/codex-settings-schema.js +109 -47
  22. package/neural-interface/lib/editor-completion-opencode.js +132 -35
  23. package/neural-interface/lib/exec-loop-command.js +8 -1
  24. package/neural-interface/lib/launch-cache.js +293 -0
  25. package/neural-interface/lib/login-start.js +524 -0
  26. package/neural-interface/lib/native-loop-providers.js +18 -3
  27. package/neural-interface/lib/native-loop-runtime.js +19 -0
  28. package/neural-interface/lib/opencode-db-schema.js +75 -0
  29. package/neural-interface/lib/opencode-history.js +16 -1
  30. package/neural-interface/lib/opencode-protocol.js +173 -0
  31. package/neural-interface/lib/opencode-provider-view.js +81 -0
  32. package/neural-interface/lib/opencode-runtime-config.js +10 -3
  33. package/neural-interface/lib/opencode-serve-records.js +606 -0
  34. package/neural-interface/lib/opencode-v2-client.js +33 -6
  35. package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
  36. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  37. package/neural-interface/lib/opencode2-client.js +760 -0
  38. package/neural-interface/lib/opencode2-proxy.js +116 -0
  39. package/neural-interface/lib/opencode2-translate.js +1008 -0
  40. package/neural-interface/lib/process-identity.js +361 -0
  41. package/neural-interface/lib/session-cache-builder.js +26 -5
  42. package/neural-interface/lib/session-title-generator.js +18 -0
  43. package/neural-interface/lib/style-guide/api.js +4 -3
  44. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  45. package/neural-interface/package-lock.json +1343 -45
  46. package/neural-interface/package.json +5 -1
  47. package/neural-interface/public/index.html +2 -2
  48. package/neural-interface/public/shared/agent-runtime-options.js +55 -11
  49. package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
  50. package/neural-interface/public/shared/assistant/asst-api.js +11 -3
  51. package/neural-interface/public/shared/assistant/asst-brain-picker.js +195 -80
  52. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  53. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  54. package/neural-interface/public/shared/assistant/asst-menu.js +77 -6
  55. package/neural-interface/public/shared/assistant/asst-panel.js +197 -33
  56. package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
  57. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  58. package/neural-interface/public/shared/assistant/asst-render.js +382 -36
  59. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  60. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  61. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  62. package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
  63. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  64. package/neural-interface/public/shared/assistant/asst-state.js +14 -0
  65. package/neural-interface/public/shared/assistant/asst-styles.js +43 -0
  66. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
  67. package/neural-interface/public/shared/cc-dropdown.js +480 -0
  68. package/neural-interface/public/shared/cdx/cdx-panel.js +13 -1
  69. package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
  70. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  71. package/neural-interface/public/shared/cdx/cdx-tabs.js +149 -35
  72. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  73. package/neural-interface/public/shared/data-cache.js +223 -0
  74. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  75. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  76. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  77. package/neural-interface/public/shared/desktop-update-view.js +15 -5
  78. package/neural-interface/public/shared/file-links.js +73 -8
  79. package/neural-interface/public/shared/html-shell.js +74 -80
  80. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  81. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  82. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +7 -2
  83. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  84. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +93 -2
  85. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
  86. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  87. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  88. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
  89. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  90. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
  91. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
  92. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -19
  93. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
  94. package/neural-interface/public/shared/opencode-providers.js +39 -0
  95. package/neural-interface/public/shared/schedules-view.js +318 -0
  96. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  97. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  98. package/neural-interface/public/shared/settings/settings-data.js +88 -0
  99. package/neural-interface/public/shared/settings/settings-ia.js +17 -0
  100. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  101. package/neural-interface/public/shared/styles.css +540 -371
  102. package/neural-interface/public/shared/ui-assistant.js +27 -5
  103. package/neural-interface/public/shared/ui-automation-studio.js +67 -27
  104. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  105. package/neural-interface/public/shared/ui-claude-panel.js +76 -8
  106. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  107. package/neural-interface/public/shared/ui-detail.js +6 -6
  108. package/neural-interface/public/shared/ui-file-links.js +1 -0
  109. package/neural-interface/public/shared/ui-image-gallery.js +6 -5
  110. package/neural-interface/public/shared/ui-invite.js +18 -2
  111. package/neural-interface/public/shared/ui-link.js +9 -9
  112. package/neural-interface/public/shared/ui-menubar.js +232 -33
  113. package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
  114. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  115. package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
  116. package/neural-interface/public/shared/ui-sessions.js +15 -4
  117. package/neural-interface/public/shared/ui-settings.js +87 -36
  118. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  119. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  120. package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
  121. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  122. package/neural-interface/public/shared/ui-skills.js +70 -12
  123. package/neural-interface/public/shared/ui-sync.js +7 -0
  124. package/neural-interface/public/shared/ui-terminal.js +36 -20
  125. package/neural-interface/public/shared/ui-trash.js +8 -1
  126. package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
  127. package/neural-interface/public/shared/ui-tutorial.js +15 -5
  128. package/neural-interface/public/shared/ui-update.js +83 -14
  129. package/neural-interface/public/shared/ui-whiteboard.js +2 -1
  130. package/neural-interface/public/variant/2d/main.js +4 -1
  131. package/neural-interface/public/variant/3d/main.js +3 -0
  132. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  133. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  134. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  135. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  136. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  137. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  138. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  139. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  140. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  141. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  142. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  143. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  144. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  145. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  146. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  147. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  148. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  149. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  150. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  151. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  152. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  153. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  154. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  155. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  156. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  157. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  158. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  159. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  160. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  161. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  162. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  163. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  164. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  165. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  166. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  167. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  168. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  169. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  170. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  171. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  172. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  173. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  174. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  175. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  176. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  177. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  178. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  179. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  180. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  181. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  182. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  183. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  184. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  185. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  186. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  187. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  188. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  189. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  190. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  191. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  192. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  193. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  194. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  195. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  196. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  197. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  198. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  199. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  200. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  201. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  202. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  203. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  204. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  205. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  206. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  207. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  208. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  209. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  210. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  211. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  212. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  213. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  214. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  215. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  216. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  217. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  218. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  219. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  220. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  221. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  222. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  223. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  224. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  225. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  226. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  227. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  228. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  229. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  230. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  231. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  232. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  233. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  234. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  235. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  236. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  237. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  238. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  239. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  240. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  241. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  242. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  243. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  244. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  245. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  246. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  247. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  248. package/neural-interface/scripts/build-vendor.mjs +84 -3
  249. package/neural-interface/server.js +1099 -286
  250. package/npm-shrinkwrap.json +2 -2
  251. package/package.json +1 -1
  252. package/skills/synabun/modules/schedule.md +1 -1
@@ -1513,6 +1513,28 @@ export function injectAssistantStyles() {
1513
1513
  .asst-dd-btnrow { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; }
1514
1514
  .asst-dd-btnrow-label { flex: 1 1 auto; min-width: 0; padding: 5px 10px; color: var(--asst-text-2); overflow-wrap: anywhere; }
1515
1515
  .asst-dd-btnrow .asst-dd-item { flex: 0 1 auto; width: auto; }
1516
+ /* A row with its own action (asst-menu.js item.action: the Sessions menu's Remove): a sm icon button at
1517
+ the row's end, over the room the row keeps free for it; shown on hover and keyboard focus, neutral at
1518
+ rest, red on hover (a destructive action). Touch screens have no hover: it always shows there. */
1519
+ .asst-dd-itemrow { position: relative; }
1520
+ .asst-dd-itemrow > .asst-dd-item { padding-right: calc(var(--asst-pill-h-sm) + 12px); }
1521
+ .asst-dd-item-action.asst-iconbtn {
1522
+ --cap-h: var(--asst-pill-h-sm);
1523
+ --cap-r: var(--asst-pill-r-sm);
1524
+ --cap-icon: 13px;
1525
+ position: absolute;
1526
+ top: calc(50% - var(--cap-h) / 2);
1527
+ right: 6px;
1528
+ opacity: 0;
1529
+ pointer-events: none;
1530
+ }
1531
+ .asst-dd-itemrow:is(:hover, :focus-within) > .asst-dd-item-action,
1532
+ .asst-dd-itemrow > .asst-dd-item.focused + .asst-dd-item-action { opacity: 1; pointer-events: auto; }
1533
+ .asst-dd-item-action.asst-iconbtn:is(:hover, :focus-visible):not(:disabled) { color: var(--asst-live); }
1534
+ @media (hover: none) { .asst-dd-item-action.asst-iconbtn { opacity: 1; pointer-events: auto; } }
1535
+ /* The Sessions menu's "Remove …?" row: the question, then Remove and Cancel, set off from the list. */
1536
+ .asst-dd-btnrow.asst-dd-confirm { justify-content: flex-end; margin: 2px 0; padding: 2px; border-radius: calc(var(--asst-r-card) - 4px); background: var(--asst-fill-1); box-shadow: inset 0 0 0 1px var(--asst-line); }
1537
+ .asst-dd-btnrow.asst-dd-confirm .asst-dd-btnrow-label { flex-basis: 100%; color: var(--asst-text); }
1516
1538
 
1517
1539
  /* ── Account menu (asst-account-menu.js) ────────────────────────
1518
1540
  Every account once: initial · name ✓ · plan or credential badge, then who it is and its
@@ -1828,6 +1850,27 @@ export function injectAssistantStyles() {
1828
1850
  .asst-lightbox-caption { max-width: 92vw; color: var(--asst-text-2); font-size: 12.5px; text-align: center; }
1829
1851
  .asst-lightbox-close { position: absolute; top: 14px; right: 14px; background: var(--asst-popover); box-shadow: 0 0 0 1px var(--asst-popover-ring); color: var(--asst-text); }
1830
1852
 
1853
+ /* ── Tray pill (#term-minimized-tray, outside the component) ─────────
1854
+ What a session minimises to, in either host: the sidepanel's tab
1855
+ (asst-sidepanel.js) and the terminal's floating tab (ui-terminal.js)
1856
+ both draw the shared .term-minimized-pill of styles.css, exactly like
1857
+ the Claude, Codex and OpenCode pills: 32px, 8px corners, a 1px
1858
+ hairline, icon · name · ✕, no geometry of its own. Only the identity is
1859
+ SynaBun's: the two-pill mark, and the cameo in its place while the
1860
+ session works (gold, with a gold hairline: its own turn or a worker it
1861
+ dispatched, asst-state.js sessionStatus); a session that needs you
1862
+ tints the mark and the hairline. The sidepanel's is a button
1863
+ (Enter/Space) with the panel's focus ring; its ✕ keeps the tray's 24px
1864
+ target. Paused while the UI is dragged and still under reduced motion
1865
+ (Motion, below): the state stays, only the glance stops. */
1866
+ .asp-session-pill .term-minimized-pill-icon .syna-cameo { display: none; }
1867
+ .asp-session-pill.asp-pill-running { border-color: var(--asst-pill-on-ring); }
1868
+ .asp-session-pill.asp-pill-running .term-minimized-pill-icon { color: var(--asst-brand); }
1869
+ .asp-session-pill.asp-pill-running .term-minimized-pill-icon > svg:not(.syna-cameo) { display: none; }
1870
+ .asp-session-pill.asp-pill-running .term-minimized-pill-icon .syna-cameo { display: inline-flex; }
1871
+ .asp-session-pill.asp-pill-attention { border-color: var(--asst-pill-warn-ring); }
1872
+ .asp-session-pill.asp-pill-attention .term-minimized-pill-icon { color: var(--asst-warn); }
1873
+
1831
1874
  /* ── Float header brain chip (lives in ui-terminal's float window) ─ */
1832
1875
  .term-float-brain-chip {
1833
1876
  display: inline-flex;
@@ -184,11 +184,16 @@ export function capitalize(text) {
184
184
  /**
185
185
  * Cut `text` in the middle so it fits `max` chars, keeping at least `minTail`
186
186
  * tail chars and cutting at token boundaries (spaces, slashes) when one is near.
187
+ * One cut, shorter than the text: never a head and a tail that overlap, and a
188
+ * token the text already shortened ("…/name") that the cut meets goes with it.
187
189
  */
188
190
  export function middleEllipsis(text, max = 96, minTail = 24) {
189
191
  const s = str(text);
190
192
  if (s.length <= max) return s;
191
193
  const budget = Math.max(max - 1, minTail + 6);
194
+ // A cut that keeps this much removes nothing: under a small `max` the budget rises to minTail + 6,
195
+ // past a short text's end, and the head and the tail overlapped ("$ np…npm test" of "$ npm test").
196
+ if (s.length <= budget + 1) return s;
192
197
  let tailStart = s.length - minTail;
193
198
  // Pull the tail back to the token boundary before it, within a third of the budget.
194
199
  const floor = s.length - Math.max(minTail, Math.floor(budget / 3) + minTail);
@@ -201,7 +206,14 @@ export function middleEllipsis(text, max = 96, minTail = 24) {
201
206
  let head = s.slice(0, Math.max(1, budget - tail.length));
202
207
  const cut = Math.max(head.lastIndexOf(' '), head.lastIndexOf('/'));
203
208
  if (cut >= head.length * 0.6) head = head.slice(0, head[cut] === '/' ? cut + 1 : cut);
204
- return `${head.trimEnd()}…${tail.trimStart()}`;
209
+ return joinCut(head, tail);
210
+ }
211
+
212
+ /** `head…tail`, one "…": a shortened token's own "…" beside the cut is dropped ("…/…/x.js" read as two cuts). */
213
+ export function joinCut(head, tail) {
214
+ const h = str(head).trimEnd().replace(/\/?…\/?$/, '').trimEnd();
215
+ const t = str(tail).trimStart().replace(/^\/?…/, '');
216
+ return `${h}…${t}`;
205
217
  }
206
218
 
207
219
  /** A path's last two folders and its basename: `shared/assistant/asst-render.js`. */
@@ -0,0 +1,480 @@
1
+ // The app's custom dropdown (DESIGN.md "Settings custom dropdown", `.cc-dropdown`): a hidden
2
+ // <input id> holds the value, a trigger button shows the choice, a menu lists the choices.
3
+ // settings-kit.js dropdown() writes the same markup for Settings; this module writes it with its
4
+ // ARIA and drives every dropdown under a root from one set of listeners, so markup drawn again
5
+ // with innerHTML needs no wiring of its own.
6
+ //
7
+ // Every dropdown in SynaBun is this one: a native <select> is not allowed (tests/no-native-select.test.mjs).
8
+ // Adding one, as the Schedules window (ui-schedules-studio.js) does:
9
+ // 1. Markup: dropdownHTML(id, [{ value, label, selected }], { labelledBy }); in Settings, settings-kit.js dropdown().
10
+ // 2. Wiring: wireDropdowns(container, { signal }) once; markup drawn later needs nothing more.
11
+ // 3. Reading: the hidden input's `.value`, and its `change` event (it bubbles), as with a <select>.
12
+ // 4. Changing: setDropdownItems(input, entries), setDropdownValue(input, value), setDropdownDisabled(input, on).
13
+ // 5. Focusing it or marking it invalid: controlOf(input) is its trigger.
14
+ //
15
+ // To the code around it, it reads like a <select>: the input's `.value` is the choice, and a
16
+ // choice the user makes fires `input` and `change` on the input (bubbling). Choices are
17
+ // `{ value, label, selected, disabled }` (labels are text), or `{ group, options }` for a heading.
18
+ // The chosen one is the last marked `selected`, else the first that is not disabled: a <select>'s
19
+ // rule, so a list rebuilt from saved data shows what was saved. setDropdownItems() is
20
+ // `select.innerHTML = …`, setDropdownValue() is `select.value = …`; neither fires an event.
21
+ //
22
+ // Keyboard and ARIA are the WAI-ARIA select-only combobox: the focus stays on the trigger and the
23
+ // highlighted option is its aria-activedescendant. Enter, Space or an arrow opens; arrows, Home,
24
+ // End, Page Up/Down and typing move; Enter or Space chooses; Escape closes without a change; Tab
25
+ // closes and moves on, the choice as it was. An open menu is placed `fixed` against its trigger,
26
+ // so a scrolling pane never cuts it off, and kept inside the box that clips it (a window).
27
+
28
+ const ARROW = '<svg class="cc-dropdown-arrow" viewBox="0 0 24 24" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>';
29
+ const GAP = 4; // between the trigger and its menu
30
+ const EDGE = 6; // the least room left between a menu and the edge of what clips it
31
+ const PAGE = 10; // options a Page Up/Down moves
32
+ const TYPE_PAUSE = 500; // ms between keys that still extend a type-ahead search
33
+
34
+ const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (c) => ({
35
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
36
+ }[c]));
37
+
38
+ // ── What decides (no DOM) ──
39
+
40
+ /** The options of a list of entries, headings left out, in their order. */
41
+ export function flatOptions(entries) {
42
+ const out = [];
43
+ for (const entry of entries || []) {
44
+ if (entry && Array.isArray(entry.options)) out.push(...entry.options.filter(Boolean));
45
+ else if (entry) out.push(entry);
46
+ }
47
+ return out;
48
+ }
49
+
50
+ /** The chosen option as a <select> picks it: the last marked selected, else the first enabled; -1 for none. */
51
+ export function chosenIndex(options) {
52
+ let last = -1;
53
+ options.forEach((option, i) => { if (option.selected) last = i; });
54
+ return last >= 0 ? last : options.findIndex(option => !option.disabled);
55
+ }
56
+
57
+ /** The first or last option that is not disabled; -1 for none. */
58
+ export function edgeIndex(options, which) {
59
+ if (which === 'first') return options.findIndex(option => !option.disabled);
60
+ for (let i = options.length - 1; i >= 0; i--) if (!options[i].disabled) return i;
61
+ return -1;
62
+ }
63
+
64
+ /** `steps` enabled options away from `from` (negative: up), stopping at the ends. From none, the first or last. */
65
+ export function stepIndex(options, from, steps) {
66
+ if (from < 0 || from >= options.length) return edgeIndex(options, steps < 0 ? 'last' : 'first');
67
+ const dir = Math.sign(steps) || 1;
68
+ let left = Math.abs(steps) || 1;
69
+ let found = from;
70
+ for (let i = from + dir; i >= 0 && i < options.length; i += dir) {
71
+ if (options[i].disabled) continue;
72
+ found = i;
73
+ if (--left === 0) break;
74
+ }
75
+ return found;
76
+ }
77
+
78
+ /**
79
+ * Type-ahead: the option whose label starts with what was typed, searching on from the highlighted
80
+ * one and round. One letter typed again moves on to the next option with that letter, as a <select> does.
81
+ */
82
+ export function matchIndex(options, typed, from = -1) {
83
+ const query = String(typed || '').toLocaleLowerCase();
84
+ if (!query) return -1;
85
+ const repeated = [...query].every(c => c === query[0]);
86
+ const needle = repeated ? query[0] : query;
87
+ const n = options.length;
88
+ const start = repeated ? from + 1 : Math.max(from, 0);
89
+ for (let k = 0; k < n; k++) {
90
+ const i = (((start + k) % n) + n) % n;
91
+ const option = options[i];
92
+ if (!option.disabled && String(option.label ?? '').trim().toLocaleLowerCase().startsWith(needle)) return i;
93
+ }
94
+ return -1;
95
+ }
96
+
97
+ // ── Markup ──
98
+
99
+ function optionHTML(id, option, index, chosen) {
100
+ const label = String(option.label ?? '');
101
+ const active = index === chosen;
102
+ return `<div class="cc-dropdown-item${active ? ' active' : ''}" id="${esc(id)}-opt-${index}" role="option" aria-selected="${active}"`
103
+ + `${option.disabled ? ' aria-disabled="true"' : ''} data-value="${esc(option.value ?? '')}" data-index="${index}" title="${esc(label)}">${esc(label)}</div>`;
104
+ }
105
+
106
+ /** The menu's options (and headings) for `entries`, the `chosen` index marked. */
107
+ export function menuHTML(id, entries, chosen = chosenIndex(flatOptions(entries))) {
108
+ let index = 0;
109
+ let group = 0;
110
+ return (entries || []).filter(Boolean).map((entry) => {
111
+ if (!Array.isArray(entry.options)) return optionHTML(id, entry, index++, chosen);
112
+ const headId = `${id}-grp-${group++}`;
113
+ const options = entry.options.filter(Boolean).map(option => optionHTML(id, option, index++, chosen)).join('');
114
+ return `<div class="cc-dropdown-optgroup" role="group" aria-labelledby="${esc(headId)}"><div class="cc-dropdown-group" id="${esc(headId)}" role="presentation">${esc(entry.group ?? '')}</div>${options}</div>`;
115
+ }).join('');
116
+ }
117
+
118
+ /**
119
+ * A dropdown: the hidden input (`id`, with `inputAttrs`) and the `.cc-dropdown` it belongs to.
120
+ * Name it with `labelledBy` (the id of its visible label) or `label`. `classes` and `attrs` go on
121
+ * the `.cc-dropdown`.
122
+ */
123
+ export function dropdownHTML(id, entries, { classes = '', labelledBy = '', label = '', attrs = '', inputAttrs = '', disabled = false } = {}) {
124
+ const options = flatOptions(entries);
125
+ const chosen = chosenIndex(options);
126
+ const current = chosen >= 0 ? options[chosen] : null;
127
+ const name = labelledBy ? ` aria-labelledby="${esc(labelledBy)}"` : label ? ` aria-label="${esc(label)}"` : '';
128
+ const text = String(current?.label ?? '');
129
+ return `<input type="hidden" id="${esc(id)}" value="${esc(current?.value ?? '')}"${disabled ? ' disabled' : ''}${inputAttrs ? ` ${inputAttrs}` : ''}>`
130
+ + `<div class="cc-dropdown${classes ? ` ${classes}` : ''}${disabled ? ' disabled' : ''}" data-for="${esc(id)}"${attrs ? ` ${attrs}` : ''}>`
131
+ + `<button type="button" class="cc-dropdown-trigger" id="${esc(id)}-trigger" role="combobox" aria-haspopup="listbox" aria-expanded="false" aria-controls="${esc(id)}-menu"${name}${disabled ? ' disabled' : ''}>`
132
+ + `<span class="cc-dropdown-value" title="${esc(text)}">${esc(text)}</span>${ARROW}</button>`
133
+ + `<div class="cc-dropdown-menu" id="${esc(id)}-menu" role="listbox"${name} tabindex="-1">${menuHTML(id, entries, chosen)}</div>`
134
+ + '</div>';
135
+ }
136
+
137
+ // ── The DOM: finding the parts ──
138
+
139
+ const partsOf = (dd) => ({
140
+ trigger: dd.querySelector(':scope > .cc-dropdown-trigger'),
141
+ menu: dd.querySelector(':scope > .cc-dropdown-menu'),
142
+ label: dd.querySelector(':scope > .cc-dropdown-trigger .cc-dropdown-value'),
143
+ input: dd.dataset.for ? document.getElementById(dd.dataset.for) : null,
144
+ });
145
+ const itemsOf = (dd) => [...(partsOf(dd).menu?.querySelectorAll('.cc-dropdown-item') || [])];
146
+ const optionsOf = (items) => items.map(el => ({ value: el.dataset.value ?? '', label: el.textContent, disabled: el.getAttribute('aria-disabled') === 'true' }));
147
+ const highlightedOf = (items) => items.findIndex(el => el.classList.contains('highlight'));
148
+ const chosenOf = (items) => items.findIndex(el => el.classList.contains('active'));
149
+ const isOpen = (dd) => !!dd?.classList.contains('open');
150
+
151
+ /** The `.cc-dropdown` of a hidden input, or null. */
152
+ export function dropdownOf(input) {
153
+ if (!input?.id) return null;
154
+ const next = input.nextElementSibling;
155
+ if (next?.classList.contains('cc-dropdown') && next.dataset.for === input.id) return next;
156
+ return document.querySelector(`.cc-dropdown[data-for="${CSS.escape(input.id)}"]`);
157
+ }
158
+
159
+ /** What takes the focus for a field: a dropdown's trigger for its hidden input, else the element itself. */
160
+ export function controlOf(el) {
161
+ const dd = el && el.type === 'hidden' ? dropdownOf(el) : null;
162
+ return (dd && partsOf(dd).trigger) || el;
163
+ }
164
+
165
+ // Markup written by settings-kit.js dropdown() has no roles or ids: they are added when it opens.
166
+ function ensureAria(dd) {
167
+ const { trigger, menu, input } = partsOf(dd);
168
+ const base = input?.id || dd.dataset.for || 'cc-dropdown';
169
+ if (!trigger.hasAttribute('role')) trigger.setAttribute('role', 'combobox');
170
+ if (!menu.id) menu.id = `${base}-menu`;
171
+ trigger.setAttribute('aria-controls', menu.id);
172
+ if (!menu.hasAttribute('role')) menu.setAttribute('role', 'listbox');
173
+ itemsOf(dd).forEach((el, i) => {
174
+ if (!el.id) el.id = `${base}-opt-${i}`;
175
+ if (!el.hasAttribute('role')) el.setAttribute('role', 'option');
176
+ el.setAttribute('aria-selected', String(el.classList.contains('active')));
177
+ });
178
+ }
179
+
180
+ // ── Choosing, opening, closing ──
181
+
182
+ function showChosen(dd, index) {
183
+ const { label, input } = partsOf(dd);
184
+ const items = itemsOf(dd);
185
+ items.forEach((el, i) => {
186
+ el.classList.toggle('active', i === index);
187
+ el.setAttribute('aria-selected', String(i === index));
188
+ });
189
+ const item = items[index];
190
+ if (input) input.value = item ? item.dataset.value ?? '' : '';
191
+ if (label) {
192
+ label.textContent = item ? item.textContent : '';
193
+ label.title = label.textContent;
194
+ }
195
+ }
196
+
197
+ function highlight(dd, index) {
198
+ const { trigger, menu } = partsOf(dd);
199
+ const items = itemsOf(dd);
200
+ items.forEach((el, i) => el.classList.toggle('highlight', i === index));
201
+ const item = items[index];
202
+ if (!item) { trigger?.removeAttribute('aria-activedescendant'); return; }
203
+ trigger?.setAttribute('aria-activedescendant', item.id);
204
+ // The menu scrolls itself; scrollIntoView would also move the panes and the window behind it.
205
+ const box = menu.getBoundingClientRect();
206
+ const row = item.getBoundingClientRect();
207
+ if (row.top < box.top + menu.clientTop) menu.scrollTop -= box.top + menu.clientTop - row.top;
208
+ else if (row.bottom > box.top + menu.clientTop + menu.clientHeight) menu.scrollTop += row.bottom - (box.top + menu.clientTop + menu.clientHeight);
209
+ }
210
+
211
+ // What a fixed menu is placed against and cut off by: the nearest ancestor that is a containing
212
+ // block for fixed boxes (a transform, a filter, containment: a floating window), else the viewport.
213
+ function fixedContainer(el) {
214
+ for (let node = el.parentElement; node && node !== document.documentElement; node = node.parentElement) {
215
+ const cs = getComputedStyle(node);
216
+ if (cs.transform !== 'none' || cs.perspective !== 'none' || cs.filter !== 'none'
217
+ || (cs.backdropFilter && cs.backdropFilter !== 'none') || (cs.webkitBackdropFilter && cs.webkitBackdropFilter !== 'none')
218
+ || /paint|layout|strict|content/.test(cs.contain || '') || (cs.containerType && cs.containerType !== 'normal')
219
+ || /transform|perspective|filter/.test(cs.willChange || '')) return node;
220
+ }
221
+ return null;
222
+ }
223
+
224
+ function place(dd) {
225
+ const { trigger, menu } = partsOf(dd);
226
+ const style = menu.style;
227
+ Object.assign(style, { position: 'fixed', left: '0px', top: '0px', right: 'auto', bottom: 'auto', width: 'auto', maxHeight: 'none', minWidth: '0px', maxWidth: 'none' });
228
+ const origin = menu.getBoundingClientRect(); // where the containing block's (0, 0) is on screen
229
+ const t = trigger.getBoundingClientRect();
230
+ let room = { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
231
+ const box = fixedContainer(dd);
232
+ if (box) {
233
+ const cs = getComputedStyle(box);
234
+ if (cs.overflowX !== 'visible' || cs.overflowY !== 'visible') {
235
+ const r = box.getBoundingClientRect();
236
+ room = { left: Math.max(room.left, r.left), top: Math.max(room.top, r.top), right: Math.min(room.right, r.right), bottom: Math.min(room.bottom, r.bottom) };
237
+ }
238
+ }
239
+ room = { left: room.left + EDGE, top: room.top + EDGE, right: room.right - EDGE, bottom: room.bottom - EDGE };
240
+ const roomWidth = Math.max(0, room.right - room.left);
241
+ style.maxWidth = `${roomWidth}px`;
242
+ style.minWidth = `${Math.min(t.width, roomWidth)}px`;
243
+ const height = menu.offsetHeight;
244
+ const below = room.bottom - t.bottom - GAP;
245
+ const above = t.top - GAP - room.top;
246
+ const down = height <= below || below >= above;
247
+ const space = Math.max(0, down ? below : above);
248
+ if (height > space) style.maxHeight = `${space}px`;
249
+ const shown = Math.min(height, space);
250
+ const width = menu.offsetWidth;
251
+ const left = Math.max(room.left, Math.min(t.left, room.right - width));
252
+ const top = down ? t.bottom + GAP : t.top - GAP - shown;
253
+ style.left = `${Math.round(left - origin.left)}px`;
254
+ style.top = `${Math.round(top - origin.top)}px`;
255
+ dd.classList.toggle('drop-up', !down);
256
+ dd._ccPlacedAt = { left: t.left, top: t.top };
257
+ }
258
+
259
+ // Has the trigger moved since its menu was placed (a pane scrolled under it)?
260
+ function moved(dd) {
261
+ const at = dd._ccPlacedAt;
262
+ const now = partsOf(dd).trigger?.getBoundingClientRect();
263
+ return !at || !now || Math.abs(now.left - at.left) > 1 || Math.abs(now.top - at.top) > 1;
264
+ }
265
+
266
+ function open(dd, index = null) {
267
+ const { trigger } = partsOf(dd);
268
+ if (!trigger || trigger.disabled || dd.classList.contains('disabled')) return;
269
+ for (const other of document.querySelectorAll('.cc-dropdown.open')) if (other !== dd) close(other);
270
+ ensureAria(dd);
271
+ dd.classList.add('open');
272
+ trigger.setAttribute('aria-expanded', 'true');
273
+ if (document.activeElement !== trigger) trigger.focus({ preventScroll: true });
274
+ place(dd);
275
+ const items = itemsOf(dd);
276
+ const options = optionsOf(items);
277
+ const chosen = chosenOf(items);
278
+ highlight(dd, index ?? (chosen >= 0 && !options[chosen].disabled ? chosen : edgeIndex(options, 'first')));
279
+ }
280
+
281
+ function close(dd, { focus = false } = {}) {
282
+ const { trigger, menu } = partsOf(dd);
283
+ dd.classList.remove('open', 'drop-up');
284
+ trigger?.setAttribute('aria-expanded', 'false');
285
+ trigger?.removeAttribute('aria-activedescendant');
286
+ for (const el of itemsOf(dd)) el.classList.remove('highlight');
287
+ menu?.removeAttribute('style');
288
+ dd._ccPlacedAt = null;
289
+ if (focus && trigger?.isConnected) trigger.focus({ preventScroll: true });
290
+ }
291
+
292
+ /** The user's choice: shown, the menu closed, the focus on the trigger, then `input` and `change` when it changed. */
293
+ function choose(dd, index) {
294
+ const items = itemsOf(dd);
295
+ const item = items[index];
296
+ const { input } = partsOf(dd);
297
+ if (!item || item.getAttribute('aria-disabled') === 'true') { close(dd, { focus: true }); return; }
298
+ const changed = !!input && input.value !== (item.dataset.value ?? '');
299
+ showChosen(dd, index);
300
+ close(dd, { focus: true });
301
+ // After the focus is back: a listener that draws the form again finds the trigger focused.
302
+ if (changed) {
303
+ input.dispatchEvent(new Event('input', { bubbles: true }));
304
+ input.dispatchEvent(new Event('change', { bubbles: true }));
305
+ }
306
+ }
307
+
308
+ // ── What the code around it calls ──
309
+
310
+ /** `select.innerHTML = …`: new choices; the chosen one by the <select> rule; an open menu stays open. No event. */
311
+ export function setDropdownItems(input, entries) {
312
+ const dd = dropdownOf(input);
313
+ if (!dd) return;
314
+ const { menu } = partsOf(dd);
315
+ const wasOpen = isOpen(dd);
316
+ const before = wasOpen ? itemsOf(dd)[highlightedOf(itemsOf(dd))]?.dataset.value : undefined;
317
+ const options = flatOptions(entries);
318
+ const chosen = chosenIndex(options);
319
+ menu.innerHTML = menuHTML(input.id, entries, chosen);
320
+ showChosen(dd, chosen);
321
+ if (!wasOpen) return;
322
+ place(dd);
323
+ const items = itemsOf(dd);
324
+ const again = before === undefined ? -1 : items.findIndex(el => el.dataset.value === before);
325
+ highlight(dd, again >= 0 ? again : chosen);
326
+ }
327
+
328
+ /** `select.value = …`: the first choice with that value, or none (the value is then ''). No event. */
329
+ export function setDropdownValue(input, value) {
330
+ const dd = dropdownOf(input);
331
+ if (!dd) { if (input) input.value = value ?? ''; return; }
332
+ showChosen(dd, itemsOf(dd).findIndex(el => el.dataset.value === String(value ?? '')));
333
+ }
334
+
335
+ /** `select.disabled = …`: the input, the trigger and the look. A disabled dropdown closes. */
336
+ export function setDropdownDisabled(input, disabled) {
337
+ const dd = dropdownOf(input);
338
+ if (input) input.disabled = !!disabled;
339
+ if (!dd) return;
340
+ if (disabled && isOpen(dd)) close(dd);
341
+ dd.classList.toggle('disabled', !!disabled);
342
+ const { trigger } = partsOf(dd);
343
+ if (trigger) trigger.disabled = !!disabled;
344
+ }
345
+
346
+ /** Closes every open dropdown under `root`; true when one was open. `focus` puts the focus back on its trigger. */
347
+ export function closeDropdowns(root = document, { focus = false } = {}) {
348
+ const open = [...root.querySelectorAll('.cc-dropdown.open')];
349
+ for (const dd of open) close(dd, { focus });
350
+ return open.length > 0;
351
+ }
352
+
353
+ /**
354
+ * Drives every `.cc-dropdown` under `root`, now and drawn later. `signal` (an AbortSignal) takes
355
+ * the listeners off again, the document's and the window's too.
356
+ */
357
+ export function wireDropdowns(root, { signal } = {}) {
358
+ const on = (target, type, handler, options = {}) => target.addEventListener(type, handler, { ...options, signal });
359
+ let typed = '';
360
+ let typedAt = 0;
361
+ let swallowSpaceUp = false;
362
+
363
+ const typeAhead = (dd, char) => {
364
+ const now = Date.now();
365
+ typed = (now - typedAt > TYPE_PAUSE ? '' : typed) + char;
366
+ typedAt = now;
367
+ const items = itemsOf(dd);
368
+ const index = matchIndex(optionsOf(items), typed, highlightedOf(items));
369
+ if (index >= 0) highlight(dd, index);
370
+ };
371
+ const typing = () => !!typed && Date.now() - typedAt <= TYPE_PAUSE;
372
+ // Each opening starts a search of its own: letters typed before it closed do not carry over.
373
+ const show = (dd, index) => { typed = ''; open(dd, index); };
374
+ const printable = (e) => e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && e.key !== ' ';
375
+
376
+ on(root, 'click', (e) => {
377
+ const item = e.target.closest?.('.cc-dropdown-item');
378
+ if (item && root.contains(item)) {
379
+ const dd = item.closest('.cc-dropdown');
380
+ if (item.getAttribute('aria-disabled') !== 'true') choose(dd, itemsOf(dd).indexOf(item));
381
+ return;
382
+ }
383
+ const trigger = e.target.closest?.('.cc-dropdown-trigger');
384
+ if (!trigger || !root.contains(trigger)) return;
385
+ const dd = trigger.closest('.cc-dropdown');
386
+ if (isOpen(dd)) close(dd, { focus: true }); else show(dd);
387
+ });
388
+
389
+ // The focus stays on the trigger while a choice is clicked.
390
+ on(root, 'mousedown', (e) => { if (e.target.closest?.('.cc-dropdown-menu')) e.preventDefault(); });
391
+ on(root, 'mouseover', (e) => {
392
+ const item = e.target.closest?.('.cc-dropdown-item');
393
+ if (!item || item.getAttribute('aria-disabled') === 'true') return;
394
+ const dd = item.closest('.cc-dropdown');
395
+ if (isOpen(dd)) highlight(dd, itemsOf(dd).indexOf(item));
396
+ });
397
+
398
+ on(root, 'keydown', (e) => {
399
+ const trigger = e.target.closest?.('.cc-dropdown-trigger');
400
+ if (!trigger || !root.contains(trigger) || trigger.disabled) return;
401
+ const dd = trigger.closest('.cc-dropdown');
402
+ const items = itemsOf(dd);
403
+ const options = optionsOf(items);
404
+ const current = highlightedOf(items);
405
+ if (!isOpen(dd)) {
406
+ if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(e.key)) {
407
+ e.preventDefault();
408
+ if (e.key === ' ') swallowSpaceUp = true;
409
+ show(dd);
410
+ } else if (e.key === 'Home' || e.key === 'End') {
411
+ e.preventDefault();
412
+ show(dd, edgeIndex(options, e.key === 'Home' ? 'first' : 'last'));
413
+ } else if (printable(e)) {
414
+ e.preventDefault();
415
+ show(dd);
416
+ typeAhead(dd, e.key);
417
+ }
418
+ return;
419
+ }
420
+ switch (e.key) {
421
+ case 'ArrowDown':
422
+ case 'ArrowUp':
423
+ e.preventDefault();
424
+ if (e.altKey && e.key === 'ArrowUp') choose(dd, current);
425
+ else highlight(dd, stepIndex(options, current, e.key === 'ArrowDown' ? 1 : -1));
426
+ break;
427
+ case 'PageDown':
428
+ case 'PageUp':
429
+ e.preventDefault();
430
+ highlight(dd, stepIndex(options, current, e.key === 'PageDown' ? PAGE : -PAGE));
431
+ break;
432
+ case 'Home':
433
+ case 'End':
434
+ e.preventDefault();
435
+ highlight(dd, edgeIndex(options, e.key === 'Home' ? 'first' : 'last'));
436
+ break;
437
+ case 'Enter':
438
+ e.preventDefault();
439
+ choose(dd, current);
440
+ break;
441
+ case ' ':
442
+ e.preventDefault();
443
+ swallowSpaceUp = true;
444
+ if (typing()) typeAhead(dd, ' '); else choose(dd, current);
445
+ break;
446
+ case 'Escape':
447
+ e.preventDefault();
448
+ e.stopPropagation();
449
+ close(dd, { focus: true });
450
+ break;
451
+ case 'Tab':
452
+ close(dd); // the focus moves on as usual; the choice stays as it was
453
+ break;
454
+ default:
455
+ if (printable(e)) { e.preventDefault(); typeAhead(dd, e.key); }
456
+ }
457
+ });
458
+ // A Space that opened or chose must not also click the trigger when it is let go.
459
+ on(root, 'keyup', (e) => {
460
+ if (e.key === ' ' && swallowSpaceUp) { swallowSpaceUp = false; e.preventDefault(); }
461
+ });
462
+
463
+ on(root, 'focusout', (e) => {
464
+ const dd = e.target.closest?.('.cc-dropdown');
465
+ if (dd && isOpen(dd) && !dd.contains(e.relatedTarget)) close(dd);
466
+ });
467
+ on(document, 'pointerdown', (e) => {
468
+ for (const dd of root.querySelectorAll('.cc-dropdown.open')) if (!dd.contains(e.target)) close(dd);
469
+ }, { capture: true });
470
+ // A pane that scrolls the trigger away from its menu closes the menu. A scroll event arrives a frame
471
+ // late, so one from before the menu opened (a click that first scrolled its trigger into view)
472
+ // finds the trigger where the menu was placed, and is let be.
473
+ on(document, 'scroll', (e) => {
474
+ for (const dd of root.querySelectorAll('.cc-dropdown.open')) {
475
+ const target = e.target === document ? document.documentElement : e.target;
476
+ if (!partsOf(dd).menu.contains(target) && target.contains?.(dd) && moved(dd)) close(dd);
477
+ }
478
+ }, { capture: true });
479
+ on(window, 'resize', () => closeDropdowns(root));
480
+ }
@@ -10,6 +10,7 @@ import { createStickyScrollController, getStickyScrollController } from '../ui-s
10
10
  import { wireFileLinks } from '../ui-file-links.js';
11
11
  import { trackWorkerEnd } from '../ui-worker-end.js';
12
12
  import { isDispatchedRun } from '../worker-end.js';
13
+ import { runAwaitsAnswer } from '../sidepanel-session-status.js';
13
14
  import { injectStyles } from './cdx-styles.js';
14
15
  import { mountContextMenu } from './cdx-context-menu.js';
15
16
  import { teardownMcpApps } from './cdx-mcp-app-host.js';
@@ -1204,6 +1205,15 @@ export function isCodexPanelOpen() {
1204
1205
  return _visible;
1205
1206
  }
1206
1207
 
1208
+ /**
1209
+ * Boot the panel without showing it (ui-sidepanel-tray.js, when a tab saved while it worked comes back
1210
+ * after a reload): the tabs that were working reattach at once (restoreTabs), so their pills say what
1211
+ * the threads do now.
1212
+ */
1213
+ export function bootCodexPanel() {
1214
+ ensurePanel();
1215
+ }
1216
+
1207
1217
  export async function toggleCodexPanel() {
1208
1218
  ensurePanel();
1209
1219
  if (_visible) {
@@ -1252,10 +1262,12 @@ export async function attachCodexAutomation(run, {
1252
1262
  }, { autoSwitch: false });
1253
1263
  }
1254
1264
  if (!tab) return { ok: false, reason: 'tab_limit' };
1255
- // A task-mode worker between turns (waiting for a follow-up) is alive, not working.
1265
+ // A task-mode worker between turns (waiting for a follow-up) is alive, not working; one whose turn ended
1266
+ // on a question needs you, like one with an approval card up (setRunning and renderPills repaint).
1256
1267
  const working = running && run.turnState !== 'idle' && run.turnState !== 'terminal';
1257
1268
  tab.automationRunId = run.runId;
1258
1269
  tab.automationActive = running;
1270
+ tab.automationAwaits = runAwaitsAnswer(run);
1259
1271
  tab.automationOwnerId = nativeLoopWindowId;
1260
1272
  tab.threadId = threadId;
1261
1273
  tab.project = run.cwd || tab.project;
@@ -16,7 +16,8 @@ import { bindExpandableCard, setCardExpanded } from './cdx-cards.js';
16
16
  import { mountMcpAppCard } from './cdx-mcp-app-host.js';
17
17
  import { OUTPUT_LIMITS, boundedText, jsonAnswer, prettyJsonFences, readableStructuredValue, fileChangeDiffs, parseUnifiedDiff, deduplicateTurnDiff } from './cdx-output-model.js';
18
18
  import { renderReadableOutput, renderReadableBlock, cleanOutputBlocks, hydrateOutputControls, observeOutputBlocks, renderDiffView, restoreDiffViews } from './cdx-output.js';
19
- import { fileLinkTitle, fillFileLinkCopy, isInsideRoots, parseFileLinkTarget } from '../file-links.js';
19
+ import { fileLinkTitle, fillFileLinkCopy, isInsideRoots, parseFileLinkTarget, readFileLink } from '../file-links.js';
20
+ import { sanitizeHtmlString } from '../assistant/asst-sanitize.js';
20
21
  export { setCardExpanded } from './cdx-cards.js';
21
22
 
22
23
  // ── Shared context (set by cdx-tabs via setRenderContext) ──
@@ -98,6 +99,11 @@ export function esc(s) {
98
99
  // streaming delta tick. Memoize so identical text — unchanged messages
99
100
  // elsewhere in the transcript that get re-rendered anyway — returns cached
100
101
  // HTML without re-parsing.
102
+ //
103
+ // What a model writes is not ours, and marked passes raw HTML through: an
104
+ // `<img onerror>` in a reply would run in SynaBun's origin with no click. Every
105
+ // Markdown sink of the panel takes md()'s output, so it goes through the
106
+ // allowlist sanitiser the Assistant, Claude and OpenCode panels use, here, once.
101
107
  const _mdCache = new Map();
102
108
  const _MD_CACHE_LIMIT = 512;
103
109
  let _mdRenderer = null;
@@ -134,6 +140,9 @@ export function md(text) {
134
140
  try { html = parser.call(renderer, prettyJsonFences(key)); }
135
141
  catch { html = markdownFallback(key); }
136
142
  }
143
+ // A page with no DOM to sanitise in, or a sanitiser that fails, gets escaped text: never raw HTML.
144
+ try { html = sanitizeHtmlString(html); }
145
+ catch { html = markdownFallback(key); }
137
146
  if (_mdCache.size >= _MD_CACHE_LIMIT) {
138
147
  const firstKey = _mdCache.keys().next().value;
139
148
  if (firstKey !== undefined) _mdCache.delete(firstKey);
@@ -409,8 +418,11 @@ function openInEditorTitle(target) {
409
418
 
410
419
  export function markProjectFileAnchors(container) {
411
420
  if (!container) return;
412
- container.querySelectorAll('a[href]').forEach((link) => {
413
- const target = projectFileTarget(link.getAttribute('href'));
421
+ // A path the sanitiser kept as written (data-file-target) is the link's target: its href, when it has one, is
422
+ // that path resolved against the page.
423
+ container.querySelectorAll('a[href], a[data-file-target]').forEach((link) => {
424
+ const read = readFileLink(link);
425
+ const target = read && projectFileTarget(read.raw);
414
426
  if (!target) return;
415
427
  link.classList.add('cxp-file-link');
416
428
  link.dataset.projectFilePath = target.path;
@@ -2724,6 +2724,10 @@ export function injectStyles() {
2724
2724
  animation: cxp-working 1.6s ease-in-out infinite;
2725
2725
  flex-shrink: 0;
2726
2726
  }
2727
+ /* A session that waits on you (an approval or a question card): the warn hairline and mark of
2728
+ the Assistant's and a detached window's pills, in place of the working look (paintTrayPill). */
2729
+ .cxp-session-pill.cxp-pill-attention { border-color: color-mix(in srgb, var(--cxp-warning) 42%, transparent); }
2730
+ .cxp-session-pill.cxp-pill-attention .term-minimized-pill-icon { color: var(--cxp-warning); }
2727
2731
  .cxp-hint {
2728
2732
  margin-top: 8px;
2729
2733
  font-size: var(--cxp-fs-xs);