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
@@ -0,0 +1,231 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Assistant — a worker's stage in its run card
3
+ // ═══════════════════════════════════════════
4
+ // While a dispatched run works (queued, starting, running a turn), its run
5
+ // card in the transcript opens a stage: the mascot acting out the run's
6
+ // scene (asst-scenes.js: its task class, or what its current tool call does),
7
+ // the scene beside it and the worker's step. The run's elapsed time has one
8
+ // place, the card's details line (asst-render.js ticks it with the stage's
9
+ // beat), so the stage repeats no clock. The stage belongs to
10
+ // the run, not to the Assistant's turn: it stays open and moving after the
11
+ // turn that dispatched the run has ended, through new turns and messages,
12
+ // card updates (it is built once and updated in place), a hidden panel (the
13
+ // rig parks off screen and comes back by itself) and a reload (the renderer
14
+ // restores the card). Waiting on you it holds a calm blocked frame; once the
15
+ // run settles (its turn done, or the run ended) it shows the final frame,
16
+ // holds it, and folds into the card's compact form; a new turn opens it
17
+ // again.
18
+ //
19
+ // One rig per open stage on the shared ticker; the accessible name (whole
20
+ // minutes) and the gaze or pop beats some scenes use run on the renderer's
21
+ // 1 Hz beat (tick()), never a loop of their own. Styles:
22
+ // asst-scene-styles.js.
23
+
24
+ import * as mascotLib from '../synabun-mascot.js';
25
+ import { isTerminalRunStatus } from './asst-state.js';
26
+ import { SCENE_SPECS, compactActivity, finalFrame, isLivePhase, phaseLine, sceneFor, sceneSvg, stageLabel, stagePhase, stepLine } from './asst-scenes.js';
27
+
28
+ export const STAGE_TIMING = Object.freeze({
29
+ settleHoldMs: 1400, // the final frame shows this long before the stage folds
30
+ foldMs: 260, // the fold, then the rig goes
31
+ sceneDwellMs: 2500, // a scene the activity picked stays at least this long
32
+ });
33
+
34
+ const GAZE = {
35
+ pointer: [[0.9, 0.25], [0.55, -0.35], [1, 0.45], [0.7, 0]],
36
+ canvas: [[0.8, -0.2], [0.95, 0.25], [0.7, 0.05]],
37
+ strip: [[0.35, 0], [0.75, 0.05], [1, 0], [0.75, -0.05]],
38
+ };
39
+
40
+ /**
41
+ * createWorkerStage(card, { t, calm, now, mascot })
42
+ * Builds the stage (closed) after the card's meta row. update(run) opens it
43
+ * for a live phase and settles it otherwise; tick(now) is the 1 Hz beat.
44
+ * Returns { el, update(run), setActivity(activity), tick(now), live(), state(), destroy() }.
45
+ */
46
+ export function createWorkerStage(card, { t = (_key, fallback) => fallback, calm = () => false, now = () => Date.now(), mascot = mascotLib } = {}) {
47
+ const wrap = document.createElement('div');
48
+ wrap.className = 'asst-run-stage-wrap';
49
+ wrap.innerHTML = '<div class="asst-run-stage" role="img">'
50
+ + '<div class="asst-run-stage-face" aria-hidden="true"></div>'
51
+ + '<div class="asst-run-stage-scene" aria-hidden="true"></div>'
52
+ + '<div class="asst-run-stage-line" aria-hidden="true"><span class="asst-run-stage-step"></span></div>'
53
+ + '<div class="asst-run-stage-sweep" aria-hidden="true"></div>'
54
+ + '</div>';
55
+ const stage = wrap.firstElementChild;
56
+ const face = stage.querySelector('.asst-run-stage-face');
57
+ const sceneHost = stage.querySelector('.asst-run-stage-scene');
58
+ const stepEl = stage.querySelector('.asst-run-stage-step');
59
+ const meta = card.querySelector(':scope > .asst-run-meta');
60
+ if (meta) meta.after(wrap); else card.appendChild(wrap);
61
+
62
+ let run = null;
63
+ let phase = null;
64
+ let open = false;
65
+ let scene = '';
66
+ let sceneAt = -Infinity;
67
+ let activity = null;
68
+ let rig = null;
69
+ let pose = '';
70
+ let final = '';
71
+ let foldTimer = 0;
72
+ let clearTimer = 0;
73
+ let beat = 0;
74
+ let destroyed = false;
75
+
76
+ const safe = (fn) => { try { return fn(); } catch { return undefined; } };
77
+
78
+ function ensureRig() {
79
+ if (rig || destroyed) return;
80
+ rig = safe(() => mascot.createMascot(face, { width: 56, height: 28, rangeX: 10, rangeY: 7, pose: 'idle' })) || null;
81
+ pose = '';
82
+ }
83
+
84
+ function dropRig() {
85
+ if (rig) safe(() => rig.destroy());
86
+ rig = null;
87
+ pose = '';
88
+ face.textContent = '';
89
+ }
90
+
91
+ function setPose(name, opts) {
92
+ const key = `${name}|${opts?.then || ''}`;
93
+ if (!rig || key === pose) return;
94
+ pose = key;
95
+ safe(() => rig.setPose(name, opts));
96
+ }
97
+
98
+ /** The scene for now; a swap waits out the dwell of the one on screen (the beat tries again), except out of the generic one. */
99
+ function syncScene(at) {
100
+ const want = sceneFor(run, activity, at);
101
+ if (want === scene) return;
102
+ if (scene && scene !== 'generic' && at - sceneAt < STAGE_TIMING.sceneDwellMs) return;
103
+ scene = want;
104
+ sceneAt = at;
105
+ sceneHost.innerHTML = sceneSvg(scene);
106
+ stage.dataset.scene = scene;
107
+ pose = ''; // the new scene's pose
108
+ }
109
+
110
+ function moving() {
111
+ return open && !final && phase !== 'waiting' && isLivePhase(phase);
112
+ }
113
+
114
+ function paint() {
115
+ if (destroyed || !run) return;
116
+ const at = now();
117
+ syncScene(at);
118
+ const live = moving();
119
+ stage.dataset.phase = final ? phase || 'done' : phase || '';
120
+ stage.toggleAttribute('data-live', live);
121
+ if (final) stage.dataset.final = final; else delete stage.dataset.final;
122
+ sceneHost.firstElementChild?.toggleAttribute('data-live', live);
123
+ if (rig && !final) setPose(phase === 'waiting' ? 'blocked' : (SCENE_SPECS[scene] || SCENE_SPECS.generic).pose);
124
+ const text = final ? phaseLine(phase, run, scene, t)
125
+ : phase === 'working' ? (stepLine(activity, t, at) || phaseLine(phase, run, scene, t))
126
+ : phaseLine(phase, run, scene, t);
127
+ if (stepEl.textContent !== text) stepEl.textContent = text;
128
+ paintLabel(at);
129
+ }
130
+
131
+ function elapsedMs(at) {
132
+ const from = new Date(run?.startedAt || run?.createdAt || 0).getTime();
133
+ if (!from) return 0;
134
+ const end = isTerminalRunStatus(run?.status) ? new Date(run.completedAt || run.updatedAt || at).getTime() : at;
135
+ return Math.max(0, end - from);
136
+ }
137
+
138
+ /** The stage's accessible name; its minutes are the only time the stage says (the card's details line shows the clock). */
139
+ function paintLabel(at) {
140
+ const label = stageLabel({ scene, phase: final ? phase : phase || 'working', title: run?.title || run?.task || '', ms: elapsedMs(at), run }, t);
141
+ if (stage.getAttribute('aria-label') !== label) stage.setAttribute('aria-label', label);
142
+ }
143
+
144
+ function openStage() {
145
+ clearTimeout(foldTimer);
146
+ clearTimeout(clearTimer);
147
+ foldTimer = 0;
148
+ clearTimer = 0;
149
+ final = '';
150
+ if (!open) {
151
+ open = true;
152
+ card.classList.add('is-staged');
153
+ }
154
+ ensureRig();
155
+ }
156
+
157
+ /** The run settled: the final frame (a one-shot on the rig), held, then the fold. */
158
+ function settle() {
159
+ const next = finalFrame(phase, run);
160
+ if (final === next) return;
161
+ final = next;
162
+ if (rig) {
163
+ if (next === 'done') safe(() => rig.setPose('success', { then: 'idle' }));
164
+ else if (next === 'failed') safe(() => rig.setPose('error', { then: 'idle' }));
165
+ else safe(() => rig.setPose('idle'));
166
+ pose = `final|${next}`;
167
+ }
168
+ clearTimeout(foldTimer);
169
+ foldTimer = setTimeout(fold, calm() ? 0 : STAGE_TIMING.settleHoldMs);
170
+ }
171
+
172
+ function fold() {
173
+ foldTimer = 0;
174
+ if (destroyed || !open) return;
175
+ open = false;
176
+ card.classList.remove('is-staged');
177
+ clearTimeout(clearTimer);
178
+ clearTimer = setTimeout(() => { clearTimer = 0; if (!open) dropRig(); }, calm() ? 0 : STAGE_TIMING.foldMs);
179
+ }
180
+
181
+ function update(next) {
182
+ if (destroyed || !next) return;
183
+ run = next;
184
+ if (isTerminalRunStatus(run.status)) activity = null; // the run is over: so is what it was doing
185
+ phase = stagePhase(run);
186
+ if (isLivePhase(phase)) openStage();
187
+ else if (open) settle();
188
+ paint();
189
+ }
190
+
191
+ /** The 1 Hz beat: the minute in the name, a scene swap the dwell held back, and the gaze or pop some scenes use. */
192
+ function tick(at = now()) {
193
+ if (destroyed || !open || !run) return;
194
+ syncScene(at);
195
+ paint();
196
+ if (!moving() || !rig || calm()) return;
197
+ beat += 1;
198
+ const spec = SCENE_SPECS[scene] || SCENE_SPECS.generic;
199
+ const path = spec.gaze ? GAZE[spec.gaze] : null;
200
+ if (path) {
201
+ const [x, y] = path[beat % path.length];
202
+ safe(() => rig.look(x, y));
203
+ if (spec.gaze === 'pointer' && beat % 2 === 0) safe(() => rig.react('click'));
204
+ }
205
+ if (spec.pop && beat % 2 === 0) safe(() => rig.react('pop'));
206
+ }
207
+
208
+ return {
209
+ el: wrap,
210
+ update,
211
+ /** What the worker is doing now, from its provider events: kept compact ({ kind, name, step, at }), never its arguments. */
212
+ setActivity(next) {
213
+ activity = run && isTerminalRunStatus(run.status) ? null : compactActivity(next, t, now());
214
+ if (open) paint();
215
+ },
216
+ tick,
217
+ /** Open (live, or holding its final frame before the fold). */
218
+ live: () => open,
219
+ /** For tests and devtools. */
220
+ state: () => ({ open, phase, scene, final, pose: rig?.pose || null, step: stepEl.textContent, label: stage.getAttribute('aria-label') || '' }),
221
+ destroy() {
222
+ if (destroyed) return;
223
+ destroyed = true;
224
+ clearTimeout(foldTimer);
225
+ clearTimeout(clearTimer);
226
+ dropRig();
227
+ card.classList.remove('is-staged');
228
+ wrap.remove();
229
+ },
230
+ };
231
+ }
@@ -4,12 +4,14 @@
4
4
  // marked turns what a brain said into HTML, and that text is not ours: a
5
5
  // quoted web page, a plan, a tool's output can carry <img onerror>, a
6
6
  // javascript: link or an <svg onload>. Every Markdown sink in the Assistant
7
- // goes through this allowlist first. The markup is parsed into an inert
7
+ // and in the Claude, Codex and OpenCode panels goes through this allowlist
8
+ // first: it is the one sanitiser, never write a second. The markup is parsed into an inert
8
9
  // <template> (nothing runs, nothing loads) and rebuilt node by node into
9
10
  // fresh elements, so nothing the parser saw survives unless it is listed
10
11
  // here: no on* or style attributes, no ids, no namespaced markup. No
11
12
  // dependency beyond file-links.js (pure), nothing touches `document` at
12
- // import time.
13
+ // import time. A transcript a panel saved and shows again goes through the
14
+ // second list at the end of this file (sanitizeStoredHtml).
13
15
 
14
16
  import { localFileHref } from '../file-links.js';
15
17
 
@@ -27,7 +29,21 @@ const SAFE_SCHEMES = new Set(['http:', 'https:', 'mailto:']);
27
29
  const CLASS_RE = /^language-[\w+#.-]{1,40}$/;
28
30
  // The panel's own generated media (readRun shows a run's images inline).
29
31
  const MEDIA_SRC_RE = /^\/api\/assistant\/runs\/[\w.-]+\/media\/\d+$/;
32
+ // An image of SynaBun's own gallery (GET /api/images/file/<name>: a screenshot, a paste, an image a CLI saved), as
33
+ // written by the model: a path of this server only (no host, scheme, query or fragment), one file name (no folder,
34
+ // no escaped slash, backslash or dot) with an image extension.
35
+ const GALLERY_SRC_RE = /^\/api\/images\/file\/((?!\.)(?:[\w.-]|%(?!2[eEfF]|5[cC])[0-9A-Fa-f]{2})+)$/;
36
+ const GALLERY_EXT_RE = /\.(?:png|jpe?g|gif|webp|bmp|svg)$/i;
37
+
38
+ /** An <img> src the Markdown list keeps: a run's own media, or an image of SynaBun's gallery. */
39
+ export function isOwnImageSrc(src) {
40
+ const value = String(src ?? '');
41
+ if (MEDIA_SRC_RE.test(value)) return true;
42
+ const gallery = GALLERY_SRC_RE.exec(value);
43
+ return !!gallery && GALLERY_EXT_RE.test(gallery[1]);
44
+ }
30
45
  const MAX_DEPTH = 64;
46
+ const ALIGNS = new Set(['left', 'center', 'right']);
31
47
 
32
48
  /** An href the Assistant may render: http(s) or mailto after resolving it, else null. */
33
49
  export function safeHref(raw, base = globalThis.document?.baseURI || 'http://localhost/') {
@@ -54,6 +70,8 @@ function copyAttributes(from, to, tag) {
54
70
  if (href) to.setAttribute('href', href);
55
71
  // A path or a file: URL does not survive as an href (it is resolved against the page, or dropped).
56
72
  // Kept as written, as data: ui-file-links.js reads it on a click and opens the file in the editor.
73
+ // A target whose scheme hides behind spaces, controls or entities (`JaVa\tScript:`), or is one a browser
74
+ // runs (javascript, vbscript, data…), is no file: that link keeps neither an href nor a target.
57
75
  const file = localFileHref(from.getAttribute('href'));
58
76
  if (file) to.setAttribute('data-file-target', file);
59
77
  const title = from.getAttribute('title');
@@ -65,6 +83,15 @@ function copyAttributes(from, to, tag) {
65
83
  const classes = String(from.getAttribute('class') || '').split(/\s+/).filter(c => CLASS_RE.test(c));
66
84
  if (classes.length) to.setAttribute('class', classes.join(' '));
67
85
  }
86
+ // A table column's alignment and a list's first number: marked writes both, and they are words and digits only.
87
+ if (tag === 'th' || tag === 'td') {
88
+ const align = String(from.getAttribute('align') || '').toLowerCase();
89
+ if (ALIGNS.has(align)) to.setAttribute('align', align);
90
+ }
91
+ if (tag === 'ol') {
92
+ const start = String(from.getAttribute('start') || '');
93
+ if (/^\d{1,9}$/.test(start)) to.setAttribute('start', start);
94
+ }
68
95
  }
69
96
 
70
97
  function clean(source, target, doc, depth) {
@@ -84,7 +111,7 @@ function clean(source, target, doc, depth) {
84
111
  }
85
112
  if (tag === 'img') {
86
113
  const src = String(node.getAttribute('src') || '');
87
- if (MEDIA_SRC_RE.test(src)) {
114
+ if (isOwnImageSrc(src)) {
88
115
  const img = doc.createElement('img');
89
116
  img.setAttribute('src', src);
90
117
  img.setAttribute('alt', node.getAttribute('alt') || '');
@@ -122,3 +149,166 @@ export function sanitizeHtmlString(html, doc = globalThis.document) {
122
149
  holder.appendChild(sanitizeHtml(html, doc));
123
150
  return holder.innerHTML;
124
151
  }
152
+
153
+ // ── Stored transcripts ──
154
+ // A side panel saves its transcript's own markup (messages, cards, buttons, data attributes, the inline SVG of
155
+ // its icons) and puts it back when the session is opened again. That markup is the panel's, so the Markdown list
156
+ // above would take it apart; but it comes back from storage, and a build from before the panels sanitised their
157
+ // Markdown saved model HTML into it as it was. So it is rebuilt here the same way, node by node into fresh
158
+ // elements, from a second list: what a panel writes is kept exactly as it was written (a normal transcript looks
159
+ // as it did), and nothing else is. No script, handler, frame or form, no id or name, no image from another site
160
+ // (a remote image is not loaded again from storage), no style that loads something or leaves the transcript.
161
+
162
+ const SVG_NS = 'http://www.w3.org/2000/svg';
163
+ const STORED_MAX_DEPTH = 128;
164
+ /** The panel's own elements, besides the Markdown ones; an element not listed is unwrapped (a <form> too). */
165
+ const STORED_TAGS = new Set([...ALLOWED_TAGS, 'div', 'section', 'header', 'footer', 'article', 'aside', 'figure', 'figcaption',
166
+ 'small', 'b', 'i', 'u', 's', 'sub', 'sup', 'mark', 'kbd', 'samp', 'var', 'abbr', 'cite', 'q', 'dfn', 'ins', 'time', 'wbr',
167
+ 'caption', 'colgroup', 'col', 'tfoot', 'dl', 'dt', 'dd', 'details', 'summary', 'label', 'button', 'input', 'textarea',
168
+ 'select', 'option', 'optgroup', 'progress', 'meter', 'img']);
169
+ const STORED_DROPPED = new Set([...DROPPED].filter(tag => tag !== 'input'));
170
+ /** The shapes of the panels' icons; any other SVG element (script, a, use, image, foreignObject, animate…) goes with what is in it. */
171
+ const STORED_SVG_TAGS = new Set(['svg', 'g', 'path', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'rect', 'title', 'desc']);
172
+ const STORED_SVG_ATTRS = new Set(['viewBox', 'width', 'height', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin',
173
+ 'stroke-dasharray', 'stroke-dashoffset', 'stroke-miterlimit', 'stroke-opacity', 'fill-opacity', 'fill-rule', 'clip-rule', 'opacity',
174
+ 'd', 'cx', 'cy', 'r', 'rx', 'ry', 'x', 'y', 'x1', 'y1', 'x2', 'y2', 'points', 'transform', 'preserveAspectRatio', 'vector-effect',
175
+ 'shape-rendering', 'focusable']);
176
+ const SVG_PAINT_ATTRS = new Set(['fill', 'stroke']);
177
+ const SVG_PAINT_RE = /^(?:none|currentColor|transparent|context-fill|context-stroke|inherit|#[0-9a-f]{3,8}|[a-z]{3,24}|(?:rgb|rgba|hsl|hsla)\(\s*[0-9.,%\s/+-]*\))$/i;
178
+ const STORED_GLOBAL_ATTRS = new Set(['class', 'title', 'role', 'hidden', 'lang', 'dir', 'tabindex', 'draggable', 'spellcheck', 'translate']);
179
+ // ARIA that names an element by id: the ids are gone, so these would point at nothing (or at the panel's own chrome).
180
+ const ARIA_ID_REFS = new Set(['aria-controls', 'aria-describedby', 'aria-labelledby', 'aria-owns', 'aria-activedescendant', 'aria-details', 'aria-errormessage', 'aria-flowto']);
181
+ const STORED_TAG_ATTRS = {
182
+ a: ['target', 'rel'],
183
+ img: ['alt', 'width', 'height', 'loading', 'decoding'],
184
+ button: ['type', 'disabled', 'value'],
185
+ input: ['type', 'value', 'checked', 'disabled', 'readonly', 'placeholder', 'min', 'max', 'step', 'maxlength', 'size', 'autocomplete'],
186
+ textarea: ['rows', 'cols', 'placeholder', 'disabled', 'readonly', 'maxlength', 'wrap', 'autocomplete'],
187
+ select: ['disabled', 'multiple', 'size'],
188
+ option: ['value', 'selected', 'disabled', 'label'],
189
+ optgroup: ['label', 'disabled'],
190
+ details: ['open'],
191
+ th: ['align', 'colspan', 'rowspan', 'scope'],
192
+ td: ['align', 'colspan', 'rowspan'],
193
+ col: ['span'],
194
+ colgroup: ['span'],
195
+ ol: ['start', 'reversed', 'type'],
196
+ li: ['value'],
197
+ time: ['datetime'],
198
+ progress: ['value', 'max'],
199
+ meter: ['value', 'min', 'max', 'low', 'high', 'optimum'],
200
+ };
201
+ const STORED_VALUES = {
202
+ target: /^_blank$/,
203
+ type: /^(?:button|submit|reset|text|search|number|checkbox|radio|range|email|url|tel|hidden|1|a|A|i|I)$/,
204
+ tabindex: /^-?\d{1,3}$/,
205
+ };
206
+ const REL_TOKENS = new Set(['noopener', 'noreferrer', 'nofollow', 'ugc', 'external']);
207
+ // Inline styles: the layout properties the panels set (a code block's position, an image preview's size, a card
208
+ // hidden or dimmed while busy, a chip's colour) and nothing that loads, positions over the page or hides behind an
209
+ // escape: a value names no function but the colour and arithmetic ones, so `background` is a colour, never an image.
210
+ const STYLE_PROPS = new Set(['display', 'position', 'width', 'height', 'min-width', 'min-height', 'max-width', 'max-height',
211
+ 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'padding', 'padding-top', 'padding-right', 'padding-bottom',
212
+ 'padding-left', 'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-color', 'border-width', 'border-style',
213
+ 'border-radius', 'box-sizing', 'opacity', 'visibility', 'pointer-events', 'cursor', 'overflow', 'overflow-x', 'overflow-y',
214
+ 'overflow-wrap', 'word-break', 'white-space', 'text-overflow', 'text-align', 'vertical-align', 'line-height', 'font-size',
215
+ 'font-weight', 'font-style', 'letter-spacing', 'text-decoration', 'color', 'background', 'background-color', 'gap', 'row-gap', 'column-gap',
216
+ 'flex', 'flex-direction', 'flex-wrap', 'flex-grow', 'flex-shrink', 'flex-basis', 'align-items', 'align-self', 'justify-content',
217
+ 'object-fit', 'tab-size']);
218
+ const STYLE_FUNCTIONS = new Set(['var', 'calc', 'rgb', 'rgba', 'hsl', 'hsla', 'min', 'max', 'clamp']);
219
+
220
+ /** A style attribute as written when every declaration is a layout one; else only those; '' when none is left. */
221
+ export function storedStyle(text) {
222
+ const kept = [];
223
+ let dropped = false;
224
+ for (const part of String(text ?? '').split(';')) {
225
+ const decl = part.trim();
226
+ if (!decl) continue;
227
+ const colon = decl.indexOf(':');
228
+ const prop = colon > 0 ? decl.slice(0, colon).trim().toLowerCase() : '';
229
+ const value = colon > 0 ? decl.slice(colon + 1).trim() : '';
230
+ const functions = [...value.matchAll(/([a-z-]*)\s*\(/gi)].map(m => m[1].toLowerCase());
231
+ const ok = (STYLE_PROPS.has(prop) || /^--[a-z0-9-]{1,64}$/.test(prop)) && value !== ''
232
+ && functions.every(fn => STYLE_FUNCTIONS.has(fn))
233
+ && !/[\\<>{}@"']|\/\*|url|expression|javascript|image|attr/i.test(value)
234
+ && (prop !== 'position' || /^(?:static|relative)$/i.test(value));
235
+ if (ok) kept.push(decl); else dropped = true;
236
+ }
237
+ if (!dropped) return String(text ?? '');
238
+ return kept.length ? `${kept.join('; ')};` : '';
239
+ }
240
+
241
+ /** An image a stored transcript may show again: a data: image, or a file of this page's own origin (a blob: of it). */
242
+ export function storedImageSrc(raw, base = globalThis.document?.baseURI || 'http://localhost/') {
243
+ const value = String(raw ?? '');
244
+ if (/^data:image\//i.test(value.replace(/[\u0000- ]/g, ''))) return value;
245
+ let url;
246
+ let page;
247
+ try { url = new URL(value, base); page = new URL(base); } catch { return null; }
248
+ if (!value.trim() || !['http:', 'https:', 'blob:'].includes(url.protocol)) return null;
249
+ return url.origin === page.origin ? value : null;
250
+ }
251
+
252
+ function storedAttribute(tag, svg, name, value, base) {
253
+ const lower = name.toLowerCase();
254
+ if (lower === 'style') {
255
+ const style = storedStyle(value);
256
+ return style || null;
257
+ }
258
+ if (/^data-[\w.-]+$/i.test(name)) return value;
259
+ if (/^aria-[a-z]+$/.test(lower)) return ARIA_ID_REFS.has(lower) ? null : value;
260
+ if (STORED_GLOBAL_ATTRS.has(lower)) return !STORED_VALUES[lower] || STORED_VALUES[lower].test(value) ? value : null;
261
+ if (svg) {
262
+ if (name === 'xmlns') return value === SVG_NS ? value : null;
263
+ // A presentation attribute is read as CSS, where an escape spells anything (u\72 l( is url(): a value with an
264
+ // escape, a comment or the word url is not kept, and a paint is a colour or a keyword, never a paint server.
265
+ if (!STORED_SVG_ATTRS.has(name) || /\\|\/\*|url/i.test(value)) return null;
266
+ return !SVG_PAINT_ATTRS.has(name) || SVG_PAINT_RE.test(value.trim()) ? value : null;
267
+ }
268
+ if (tag === 'a' && lower === 'href') return safeHref(value, base) ? value : null;
269
+ if (tag === 'img' && lower === 'src') return storedImageSrc(value, base);
270
+ if (!(STORED_TAG_ATTRS[tag] || []).includes(lower)) return null;
271
+ if (lower === 'rel') {
272
+ const tokens = value.split(/\s+/).filter(token => REL_TOKENS.has(token.toLowerCase()));
273
+ return tokens.length ? tokens.join(' ') : null;
274
+ }
275
+ return !STORED_VALUES[lower] || STORED_VALUES[lower].test(value) ? value : null;
276
+ }
277
+
278
+ function cleanStored(source, target, doc, depth, base) {
279
+ for (const node of source.childNodes) {
280
+ if (node.nodeType === 3) { target.appendChild(doc.createTextNode(node.data)); continue; }
281
+ if (node.nodeType !== 1) continue;
282
+ const tag = node.localName;
283
+ const svg = node.namespaceURI === SVG_NS;
284
+ if (depth >= STORED_MAX_DEPTH || (!svg && (node.namespaceURI !== HTML_NS || tag === 'math'))) {
285
+ const text = textOf(node);
286
+ if (text) target.appendChild(doc.createTextNode(text));
287
+ continue;
288
+ }
289
+ if (svg ? !STORED_SVG_TAGS.has(tag) : STORED_DROPPED.has(tag)) continue;
290
+ if (!svg && !STORED_TAGS.has(tag)) { cleanStored(node, target, doc, depth + 1, base); continue; } // unwrap
291
+ if (tag === 'img' && !storedImageSrc(node.getAttribute('src'), base)) continue;
292
+ const el = svg ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag);
293
+ for (const name of node.getAttributeNames()) {
294
+ const value = storedAttribute(tag, svg, name, node.getAttribute(name), base);
295
+ if (value !== null) el.setAttribute(name, value);
296
+ }
297
+ // A link that opens a tab never hands that tab this page.
298
+ if (tag === 'a' && el.getAttribute('target') === '_blank') {
299
+ const rel = new Set(String(el.getAttribute('rel') || '').split(/\s+/).filter(Boolean));
300
+ if (!rel.has('noopener') || !rel.has('noreferrer')) el.setAttribute('rel', [...rel, 'noopener', 'noreferrer'].filter((t, i, all) => all.indexOf(t) === i).join(' '));
301
+ }
302
+ cleanStored(node, el, doc, depth + 1, base);
303
+ target.appendChild(el);
304
+ }
305
+ }
306
+
307
+ /** A stored transcript's markup → a DocumentFragment of fresh nodes from the stored-transcript list. */
308
+ export function sanitizeStoredHtml(html, doc = globalThis.document) {
309
+ const tpl = doc.createElement('template');
310
+ tpl.innerHTML = String(html ?? '');
311
+ const out = doc.createDocumentFragment();
312
+ cleanStored(tpl.content, out, doc, 0, doc.baseURI || globalThis.document?.baseURI || 'http://localhost/');
313
+ return out;
314
+ }