synabun 2.0.0 → 2.0.5

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 (334) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/THIRD-PARTY-LICENSES.md +46 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +2 -2
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +20 -0
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/start-launcher.js +88 -1
  14. package/mcp-server/package-lock.json +2 -2
  15. package/mcp-server/package.json +1 -3
  16. package/neural-interface/i18n/en.json +1500 -153
  17. package/neural-interface/i18n/pt-BR.json +1498 -152
  18. package/neural-interface/lib/assistant-api.js +62 -6
  19. package/neural-interface/lib/assistant-brains/claude.js +4 -0
  20. package/neural-interface/lib/assistant-catalog.js +179 -27
  21. package/neural-interface/lib/assistant-config.js +31 -2
  22. package/neural-interface/lib/assistant-dispatch.js +62 -13
  23. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  24. package/neural-interface/lib/assistant-memory.js +24 -10
  25. package/neural-interface/lib/assistant-persona.js +6 -2
  26. package/neural-interface/lib/assistant-pricing.js +21 -6
  27. package/neural-interface/lib/assistant-runtime.js +181 -12
  28. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  29. package/neural-interface/lib/assistant-test-density.js +142 -0
  30. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  31. package/neural-interface/lib/assistant-user-context.js +908 -0
  32. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  33. package/neural-interface/lib/assistant-user-learn.js +404 -0
  34. package/neural-interface/lib/assistant-user-threads.js +226 -0
  35. package/neural-interface/lib/assistant-user-writer.js +181 -0
  36. package/neural-interface/lib/browser-background-tab.js +134 -0
  37. package/neural-interface/lib/browser-screenshot.js +678 -61
  38. package/neural-interface/lib/claude-agent-bridge.js +33 -6
  39. package/neural-interface/lib/claude-model-catalog.js +30 -2
  40. package/neural-interface/lib/claude-temporary.js +46 -7
  41. package/neural-interface/lib/cloudflared/install.js +533 -0
  42. package/neural-interface/lib/cloudflared/manager.js +545 -0
  43. package/neural-interface/lib/cloudflared/pins.js +115 -0
  44. package/neural-interface/lib/cloudflared/pins.json +63 -0
  45. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  46. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  47. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  48. package/neural-interface/lib/editor-completion.js +714 -0
  49. package/neural-interface/lib/editor-config.js +149 -0
  50. package/neural-interface/lib/editor-files-api.js +268 -0
  51. package/neural-interface/lib/editor-git-api.js +134 -0
  52. package/neural-interface/lib/file-explorer-api.js +1039 -0
  53. package/neural-interface/lib/file-explorer-system.js +355 -0
  54. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  55. package/neural-interface/lib/file-explorer-watch.js +239 -0
  56. package/neural-interface/lib/git-diff-api.js +103 -0
  57. package/neural-interface/lib/git-read.js +202 -0
  58. package/neural-interface/lib/greeting/api.js +89 -0
  59. package/neural-interface/lib/greeting/budgets.js +90 -0
  60. package/neural-interface/lib/greeting/engine.js +461 -0
  61. package/neural-interface/lib/greeting/facts.js +320 -0
  62. package/neural-interface/lib/greeting/sources.js +252 -0
  63. package/neural-interface/lib/greeting/writer.js +139 -0
  64. package/neural-interface/lib/http-guards.js +189 -13
  65. package/neural-interface/lib/machine-names.js +88 -0
  66. package/neural-interface/lib/memory-api.js +31 -7
  67. package/neural-interface/lib/native-loop-providers.js +10 -5
  68. package/neural-interface/lib/plugin-installer.js +4 -2
  69. package/neural-interface/lib/project-storage.js +5 -7
  70. package/neural-interface/lib/request-gate.js +589 -0
  71. package/neural-interface/lib/ui-blob-api.js +108 -0
  72. package/neural-interface/lib/ui-blob-store.js +68 -0
  73. package/neural-interface/package-lock.json +701 -2
  74. package/neural-interface/package.json +13 -2
  75. package/neural-interface/public/install-app.html +9 -6
  76. package/neural-interface/public/offline.html +3 -2
  77. package/neural-interface/public/onboarding.html +12 -1
  78. package/neural-interface/public/shared/agent-runtime-options.js +22 -6
  79. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  80. package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
  81. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  82. package/neural-interface/public/shared/assistant/asst-mascot.js +18 -58
  83. package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
  84. package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
  85. package/neural-interface/public/shared/assistant/asst-render.js +162 -19
  86. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  87. package/neural-interface/public/shared/assistant/asst-route.js +23 -329
  88. package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
  89. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  90. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  91. package/neural-interface/public/shared/assistant/asst-state.js +185 -1
  92. package/neural-interface/public/shared/assistant/asst-styles.js +158 -4
  93. package/neural-interface/public/shared/blob-store.js +111 -22
  94. package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
  95. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  96. package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
  97. package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
  98. package/neural-interface/public/shared/constants.js +6 -1
  99. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  100. package/neural-interface/public/shared/cp/cp-styles.js +15 -0
  101. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  102. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  103. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  104. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  105. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  106. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  107. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  108. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  109. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  110. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  111. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  112. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  113. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  114. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  115. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  116. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  117. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  118. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  119. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  120. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  121. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  122. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  123. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  124. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  125. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  126. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  127. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  128. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  129. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  130. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  131. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  132. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  133. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  134. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  135. package/neural-interface/public/shared/file-links.js +339 -0
  136. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  137. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  138. package/neural-interface/public/shared/html-shell.js +160 -165
  139. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  140. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  141. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
  142. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  143. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
  144. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
  145. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  146. package/neural-interface/public/shared/pwa-install-page.js +29 -1
  147. package/neural-interface/public/shared/pwa-install.css +3 -3
  148. package/neural-interface/public/shared/pwa-install.js +100 -1
  149. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  150. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  151. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  152. package/neural-interface/public/shared/settings/settings-ia.js +18 -4
  153. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  154. package/neural-interface/public/shared/start-bridge.js +238 -19
  155. package/neural-interface/public/shared/styles.css +1414 -721
  156. package/neural-interface/public/shared/synabun-mascot.js +20 -56
  157. package/neural-interface/public/shared/tunnel-view.js +167 -0
  158. package/neural-interface/public/shared/ui-claude-panel.js +157 -16
  159. package/neural-interface/public/shared/ui-detail.js +15 -0
  160. package/neural-interface/public/shared/ui-explorer.js +22 -0
  161. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  162. package/neural-interface/public/shared/ui-file-links.js +103 -0
  163. package/neural-interface/public/shared/ui-help.js +26 -0
  164. package/neural-interface/public/shared/ui-invite.js +126 -116
  165. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  166. package/neural-interface/public/shared/ui-loading.js +159 -15
  167. package/neural-interface/public/shared/ui-menubar.js +12 -30
  168. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  169. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  170. package/neural-interface/public/shared/ui-navbar.js +45 -21
  171. package/neural-interface/public/shared/ui-settings.js +455 -488
  172. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  173. package/neural-interface/public/shared/ui-sync.js +16 -0
  174. package/neural-interface/public/shared/ui-terminal.js +23 -5
  175. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  176. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  177. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  178. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  179. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  180. package/neural-interface/public/shared/worker-end.js +251 -0
  181. package/neural-interface/public/sw.js +2 -2
  182. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  183. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  184. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  185. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  186. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  187. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  188. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  189. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  190. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  191. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  192. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  193. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  194. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  195. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  196. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  197. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  198. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  199. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  200. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  201. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  202. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  203. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  204. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  205. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  206. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  207. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  208. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  209. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  210. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  211. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  212. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  213. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  214. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  215. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  216. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  217. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  218. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  219. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  220. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  221. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  222. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  223. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  224. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  225. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  226. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  227. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  228. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  229. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  230. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  231. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  232. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  233. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  234. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  235. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  236. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  237. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  238. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  239. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  240. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  241. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  242. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  243. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  244. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  245. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  246. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  247. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  248. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  249. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  250. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  251. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  252. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  253. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  254. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  255. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  256. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  257. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  258. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  259. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  260. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  261. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  262. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  263. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  264. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  265. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  266. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  267. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  268. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  269. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  270. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  271. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  272. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  273. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  274. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  275. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  276. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  277. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  278. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  279. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  280. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  281. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  282. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  283. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  284. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  285. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  286. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  287. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  288. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  289. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  290. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  291. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  292. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  293. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  294. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  295. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  296. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  297. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  298. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  299. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  300. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  301. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  302. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  303. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  304. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  305. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  306. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  307. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  308. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  309. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  310. package/neural-interface/scripts/build-vendor.mjs +78 -2
  311. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  312. package/neural-interface/server.js +873 -1028
  313. package/neural-interface/templates/rulesets/README.md +1 -1
  314. package/package.json +2 -8
  315. package/setup.js +44 -7
  316. package/benchmarks/browser-live-results.json +0 -79
  317. package/benchmarks/browser-v2-results.json +0 -258
  318. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  319. package/neural-interface/public/category-logos/claude.svg +0 -1
  320. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  321. package/neural-interface/public/category-logos/openai.svg +0 -1
  322. package/neural-interface/public/category-logos/synabun.png +0 -0
  323. package/neural-interface/public/claude.svg +0 -1
  324. package/neural-interface/public/gallery.svg +0 -6
  325. package/neural-interface/public/leonardoai.svg +0 -1
  326. package/neural-interface/public/mcp.svg +0 -4
  327. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  328. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  329. package/neural-interface/public/sessionicon.svg +0 -8
  330. package/neural-interface/public/toolbar-album.svg +0 -8
  331. package/neural-interface/public/toolbar-question.svg +0 -6
  332. package/neural-interface/public/toolbar-sessions.svg +0 -6
  333. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  334. package/skills/synabun/synabunicon.png +0 -0
@@ -0,0 +1,212 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: commands
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // One list of what the editor can be told to do: an id, the i18n key of its
6
+ // name, the keys that run it, and the function. The keymap is built from it,
7
+ // and so are the shortcuts popover and the command palette. Keys follow
8
+ // VS Code; `Mod` is Cmd on macOS and Ctrl elsewhere.
9
+ //
10
+ // A command's `run` gets `{ cm, view, actions }`: the vendored CodeMirror
11
+ // bundle, the editor view, and what the panel offers (save, find, wrap). It
12
+ // returns true when it did something. No DOM and no imports: loaded by
13
+ // `node --test` as it is.
14
+
15
+ /**
16
+ * @typedef {object} EditorCommand
17
+ * @property {string} id
18
+ * @property {string} titleKey i18n key of its name
19
+ * @property {string[]} [keys] CodeMirror key names, the first one is the one shown
20
+ * @property {string[]} [macKeys] further keys that run it on macOS only
21
+ * @property {{ code: string | string[], alt?: boolean, shift?: boolean }} [physical] a key matched by its position
22
+ * on the keyboard instead of the character it types: Alt with a letter types a symbol on macOS, so its name is
23
+ * no use. Several codes: the key sits in another place on some keyboards
24
+ * @property {string} [gesture] a mouse gesture, for the shortcuts list only
25
+ * @property {string} [scope] where the keys work: 'editor' (default) or 'editor search-panel'
26
+ * @property {boolean} [passive] the key is the command's only when it acted: otherwise it does what it always did
27
+ * @property {boolean} [first] its keys are tried before the keys of commands that are not marked
28
+ * @property {string} [feature] it belongs to a feature the server may not have ('ai', 'git'): listed only when it does
29
+ * @property {boolean} [palette] false: not in the command palette (it acts on something a list takes the keyboard from)
30
+ * @property {(ctx: { cm: object, view: object, actions: object }) => boolean} [run]
31
+ */
32
+
33
+ // Tab: accept the open completion, indent the selected lines, or insert one indent unit at the caret.
34
+ function tab({ cm, view }) {
35
+ const { state } = view;
36
+ if (state.readOnly) return false;
37
+ if (cm.completionStatus(state) === 'active') return cm.acceptCompletion(view);
38
+ if (state.selection.ranges.some((range) => !range.empty)) return cm.indentMore(view);
39
+ view.dispatch(state.replaceSelection(state.facet(cm.indentUnit)), { scrollIntoView: true, userEvent: 'input' });
40
+ return true;
41
+ }
42
+
43
+ /** @type {EditorCommand[]} */
44
+ export const EDITOR_COMMANDS = [
45
+ // Ctrl-S saves on macOS too, as it did before CodeMirror.
46
+ { id: 'file.save', titleKey: 'editor.command.save', keys: ['Mod-s'], macKeys: ['Ctrl-s'], scope: 'editor search-panel', run: ({ actions }) => { actions.save(); return true; } },
47
+
48
+ { id: 'find.open', titleKey: 'editor.command.find', keys: ['Mod-f'], scope: 'editor search-panel', run: ({ view, actions }) => actions.openFind(view) },
49
+ { id: 'find.replace', titleKey: 'editor.command.replace', keys: ['Mod-Alt-f', 'Mod-h'], scope: 'editor search-panel', run: ({ view, actions }) => actions.openReplace(view) },
50
+ { id: 'find.next', titleKey: 'editor.command.findNext', keys: ['F3'], scope: 'editor search-panel', run: ({ view, actions }) => actions.findNext(view) },
51
+ { id: 'find.previous', titleKey: 'editor.command.findPrevious', keys: ['Shift-F3'], scope: 'editor search-panel', run: ({ view, actions }) => actions.findPrevious(view) },
52
+ { id: 'goto.line', titleKey: 'editor.command.goToLine', keys: ['Mod-g'], scope: 'editor search-panel', run: ({ view, actions }) => actions.openGoToLine(view) },
53
+
54
+ { id: 'edit.undo', titleKey: 'editor.command.undo', keys: ['Mod-z'], run: ({ cm, view }) => cm.undo(view) },
55
+ { id: 'edit.redo', titleKey: 'editor.command.redo', keys: ['Mod-Shift-z', 'Mod-y'], run: ({ cm, view }) => cm.redo(view) },
56
+
57
+ { id: 'select.nextOccurrence', titleKey: 'editor.command.selectNextOccurrence', keys: ['Mod-d'], run: ({ cm, view }) => cm.selectNextOccurrence(view) },
58
+ { id: 'select.allOccurrences', titleKey: 'editor.command.selectAllOccurrences', keys: ['Mod-Shift-l'], run: ({ cm, view }) => cm.selectSelectionMatches(view) },
59
+
60
+ { id: 'line.copyDown', titleKey: 'editor.command.copyLineDown', keys: ['Shift-Alt-ArrowDown'], run: ({ cm, view }) => cm.copyLineDown(view) },
61
+ { id: 'line.copyUp', titleKey: 'editor.command.copyLineUp', keys: ['Shift-Alt-ArrowUp'], run: ({ cm, view }) => cm.copyLineUp(view) },
62
+ { id: 'line.moveDown', titleKey: 'editor.command.moveLineDown', keys: ['Alt-ArrowDown'], run: ({ cm, view }) => cm.moveLineDown(view) },
63
+ { id: 'line.moveUp', titleKey: 'editor.command.moveLineUp', keys: ['Alt-ArrowUp'], run: ({ cm, view }) => cm.moveLineUp(view) },
64
+ { id: 'line.delete', titleKey: 'editor.command.deleteLine', keys: ['Mod-Shift-k'], run: ({ cm, view }) => cm.deleteLine(view) },
65
+
66
+ { id: 'comment.line', titleKey: 'editor.command.toggleLineComment', keys: ['Mod-/'], run: ({ cm, view }) => cm.toggleComment(view) },
67
+ { id: 'comment.block', titleKey: 'editor.command.toggleBlockComment', keys: ['Shift-Alt-a'], physical: { code: 'KeyA', alt: true, shift: true }, run: ({ cm, view }) => cm.toggleBlockComment(view) },
68
+
69
+ { id: 'indent.more', titleKey: 'editor.command.indent', keys: ['Tab', 'Mod-]'], run: (ctx) => (ctx.key === 'Tab' ? tab(ctx) : ctx.cm.indentMore(ctx.view)) },
70
+ { id: 'indent.less', titleKey: 'editor.command.outdent', keys: ['Shift-Tab', 'Mod-['], run: ({ cm, view }) => cm.indentLess(view) },
71
+
72
+ { id: 'cursor.add', titleKey: 'editor.command.addCursor', gesture: 'Alt-Click' },
73
+ { id: 'cursor.addBelow', titleKey: 'editor.command.addCursorBelow', keys: ['Mod-Alt-ArrowDown'], run: ({ cm, view }) => cm.addCursorBelow(view) },
74
+ { id: 'cursor.addAbove', titleKey: 'editor.command.addCursorAbove', keys: ['Mod-Alt-ArrowUp'], run: ({ cm, view }) => cm.addCursorAbove(view) },
75
+ { id: 'select.column', titleKey: 'editor.command.columnSelection', gesture: 'Shift-Alt-Drag' },
76
+
77
+ { id: 'fold.fold', titleKey: 'editor.command.fold', keys: ['Mod-Shift-['], run: ({ cm, view }) => cm.foldCode(view) },
78
+ { id: 'fold.unfold', titleKey: 'editor.command.unfold', keys: ['Mod-Shift-]'], run: ({ cm, view }) => cm.unfoldCode(view) },
79
+ { id: 'bracket.jump', titleKey: 'editor.command.jumpToBracket', keys: ['Mod-Shift-\\'], run: ({ cm, view }) => cm.cursorMatchingBracket(view) },
80
+
81
+ { id: 'view.wordWrap', titleKey: 'editor.command.toggleWordWrap', keys: ['Alt-z'], physical: { code: 'KeyZ', alt: true }, run: ({ actions }) => { actions.toggleWordWrap(); return true; } },
82
+ // The rest of the View menu: no keys of their own, they are run from the menu or the palette.
83
+ { id: 'view.minimap', titleKey: 'editor.command.toggleMinimap', run: ({ actions }) => { actions.toggleMinimap?.(); return true; } },
84
+ { id: 'view.indentGuides', titleKey: 'editor.command.toggleIndentGuides', run: ({ actions }) => { actions.toggleIndentGuides?.(); return true; } },
85
+ { id: 'view.whitespace', titleKey: 'editor.command.cycleWhitespace', run: ({ actions }) => { actions.cycleWhitespace?.(); return true; } },
86
+ { id: 'view.fontLarger', titleKey: 'editor.command.fontLarger', run: ({ actions }) => { actions.fontLarger?.(); return true; } },
87
+ { id: 'view.fontSmaller', titleKey: 'editor.command.fontSmaller', run: ({ actions }) => { actions.fontSmaller?.(); return true; } },
88
+ { id: 'view.fontReset', titleKey: 'editor.command.fontReset', run: ({ actions }) => { actions.fontReset?.(); return true; } },
89
+
90
+ // The palette and go-to-file. Mod-P and Mod-Shift-P are the browser's "print": the editor takes them while it
91
+ // has the keyboard. F1 is the palette's first key.
92
+ { id: 'palette.commands', titleKey: 'editor.command.commandPalette', keys: ['F1', 'Mod-Shift-p'], scope: 'editor search-panel', palette: false, run: ({ actions }) => { actions.openPalette?.(); return true; } },
93
+ { id: 'palette.files', titleKey: 'editor.command.goToFile', keys: ['Mod-p'], scope: 'editor search-panel', run: ({ actions }) => { actions.openGoToFile?.(); return true; } },
94
+
95
+ // Problems the editor found in the file (editor-lint.js), and the file against the last commit (editor-git.js).
96
+ { id: 'lint.next', titleKey: 'editor.command.nextProblem', keys: ['F8'], run: ({ view, actions }) => actions.nextProblem?.(view) === true },
97
+ { id: 'lint.previous', titleKey: 'editor.command.previousProblem', keys: ['Shift-F8'], run: ({ view, actions }) => actions.previousProblem?.(view) === true },
98
+ { id: 'git.diff', titleKey: 'editor.command.toggleDiff', feature: 'git', run: ({ actions }) => { actions.toggleDiff?.(); return true; } },
99
+
100
+ // Tabs. A browser keeps Mod-W, Mod-T and Ctrl-Tab for its own tabs and never hands them to a page: these are
101
+ // keys it leaves alone. Matched by position, as Alt with a letter or a sign types another character on macOS.
102
+ { id: 'tab.next', titleKey: 'editor.command.nextTab', keys: ['Alt-.'], physical: { code: 'Period', alt: true }, run: ({ actions }) => { actions.nextTab?.(); return true; } },
103
+ { id: 'tab.previous', titleKey: 'editor.command.previousTab', keys: ['Alt-,'], physical: { code: 'Comma', alt: true }, run: ({ actions }) => { actions.previousTab?.(); return true; } },
104
+ { id: 'tab.close', titleKey: 'editor.command.closeTab', keys: ['Alt-w'], physical: { code: 'KeyW', alt: true }, run: ({ actions }) => { actions.closeTab?.(); return true; } },
105
+ { id: 'view.dismiss', titleKey: 'editor.command.dismiss', keys: ['Escape'], scope: 'editor search-panel', passive: true, run: ({ view, actions }) => actions.dismiss(view) },
106
+
107
+ // AI autocomplete (editor-ai.js). Each key is the suggestion's only while there is one: Tab indents, Mod-→
108
+ // moves the caret and Esc closes the find bar as always when there is none.
109
+ { id: 'ai.accept', titleKey: 'editor.command.aiAccept', keys: ['Tab'], feature: 'ai', first: true, passive: true, palette: false, run: ({ view, actions }) => actions.aiAccept?.(view) === true },
110
+ { id: 'ai.acceptWord', titleKey: 'editor.command.aiAcceptWord', keys: ['Mod-ArrowRight'], feature: 'ai', first: true, passive: true, palette: false, run: ({ view, actions }) => actions.aiAcceptWord?.(view) === true },
111
+ { id: 'ai.dismiss', titleKey: 'editor.command.aiDismiss', keys: ['Escape'], feature: 'ai', first: true, passive: true, palette: false, run: ({ view, actions }) => actions.aiDismiss?.(view) === true },
112
+ { id: 'ai.trigger', titleKey: 'editor.command.aiTrigger', keys: ['Alt-\\'], physical: { code: ['Backslash', 'IntlBackslash'], alt: true }, feature: 'ai', passive: true, run: ({ view, actions }) => actions.aiTrigger?.(view) === true },
113
+ ];
114
+
115
+ /**
116
+ * CodeMirror key bindings for the commands that have keys. Each calls preventDefault whether or not the command
117
+ * did something, so a key the editor owns never reaches the browser (Mod-S never opens "Save page"). A command
118
+ * marked `passive` only prevents the default when it acted: Escape is left alone when there is nothing to close.
119
+ * Commands with a `physical` key are left out: physicalKeyHandler() runs those.
120
+ * @param {EditorCommand[]} commands
121
+ * @param {{ cm: object, actions: object }} ctx
122
+ */
123
+ export function commandKeymap(commands, { cm, actions }) {
124
+ const bindings = [];
125
+ // A key that several commands share is tried in this order: the ones marked `first`, then the rest as listed.
126
+ const ordered = [...commands.filter((command) => command.first), ...commands.filter((command) => !command.first)];
127
+ for (const command of ordered) {
128
+ if (!command.run || !command.keys || command.physical) continue;
129
+ const bind = (key, platform) => {
130
+ const binding = { [platform]: key, run: (view) => command.run({ cm, view, actions, key }) === true };
131
+ if (!command.passive) binding.preventDefault = true;
132
+ if (command.scope) binding.scope = command.scope;
133
+ bindings.push(binding);
134
+ };
135
+ for (const key of command.keys) bind(key, 'key');
136
+ for (const key of command.macKeys || []) bind(key, 'mac');
137
+ }
138
+ return bindings;
139
+ }
140
+
141
+ /**
142
+ * A keydown handler for the commands bound to a physical key. Returns true (the event was handled) when one ran;
143
+ * a `passive` command that did nothing leaves the key to type what it types.
144
+ * @param {EditorCommand[]} commands
145
+ * @param {{ cm: object, actions: object }} ctx
146
+ * @returns {(event: KeyboardEvent, view: object) => boolean}
147
+ */
148
+ export function physicalKeyHandler(commands, { cm, actions }) {
149
+ const physical = commands.filter((command) => command.run && command.physical);
150
+ return (event, view) => {
151
+ if (event.ctrlKey || event.metaKey) return false;
152
+ for (const command of physical) {
153
+ const { code, alt = false, shift = false } = command.physical;
154
+ if (![].concat(code).includes(event.code) || event.altKey !== alt || event.shiftKey !== shift) continue;
155
+ const acted = command.run({ cm, view, actions, key: command.keys?.[0] }) === true;
156
+ if (acted || !command.passive) return true;
157
+ }
158
+ return false;
159
+ };
160
+ }
161
+
162
+ const MAC_SYMBOLS = { Mod: '⌘', Meta: '⌘', Cmd: '⌘', Ctrl: '⌃', Alt: '⌥', Shift: '⇧' };
163
+ const MAC_ORDER = ['Ctrl', 'Alt', 'Shift', 'Mod', 'Meta', 'Cmd'];
164
+ const PC_NAMES = { Mod: 'Ctrl', Meta: 'Win', Cmd: 'Ctrl' };
165
+ const KEY_NAMES = {
166
+ ArrowUp: '↑', ArrowDown: '↓', ArrowLeft: '←', ArrowRight: '→',
167
+ Escape: 'Esc', Enter: 'Enter', Tab: 'Tab', Backspace: 'Backspace', Delete: 'Del', Space: 'Space',
168
+ };
169
+
170
+ /**
171
+ * A key the way it is written for the user: `Mod-Shift-k` → `⇧⌘K` on macOS, `Ctrl+Shift+K` elsewhere.
172
+ * @param {string} key a CodeMirror key name, or a gesture such as `Alt-Click`
173
+ * @param {boolean} mac
174
+ * @param {(word: string) => string} [word] translates the last part of a gesture (`Click`, `Drag`)
175
+ */
176
+ export function formatKey(key, mac, word = (w) => w) {
177
+ const parts = String(key).split('-');
178
+ // A trailing "-" is the minus key itself.
179
+ let last = parts.pop();
180
+ if (last === '' && parts.length) { parts.pop(); last = '-'; }
181
+ const name = KEY_NAMES[last] || (last.length === 1 ? last.toUpperCase() : word(last));
182
+ if (mac) {
183
+ const mods = parts.sort((a, b) => MAC_ORDER.indexOf(a) - MAC_ORDER.indexOf(b)).map((m) => MAC_SYMBOLS[m] || m);
184
+ return mods.join('') + name;
185
+ }
186
+ return [...parts.map((m) => PC_NAMES[m] || m), name].join('+');
187
+ }
188
+
189
+ /**
190
+ * The rows of the shortcuts list: every command that has a key or a gesture, with the first of its keys.
191
+ * @param {EditorCommand[]} [commands]
192
+ * @param {{ features?: string[] }} [o] the features this server has: a command of another feature is not listed
193
+ * @returns {{ id: string, titleKey: string, key: string, gesture: boolean }[]}
194
+ */
195
+ export function shortcutRows(commands = EDITOR_COMMANDS, { features = [] } = {}) {
196
+ return commands
197
+ .filter((command) => !command.feature || features.includes(command.feature))
198
+ .filter((command) => command.gesture || command.keys?.length)
199
+ .map((command) => ({ id: command.id, titleKey: command.titleKey, key: command.gesture || command.keys[0], gesture: !!command.gesture }));
200
+ }
201
+
202
+ /**
203
+ * Run a command by its id (the palette does). False when there is no such command, or it has nothing to run.
204
+ * @param {string} id
205
+ * @param {{ cm: object, view: object, actions: object }} ctx
206
+ * @param {EditorCommand[]} [commands]
207
+ */
208
+ export function runCommand(id, ctx, commands = EDITOR_COMMANDS) {
209
+ const command = commands.find((entry) => entry.id === id && typeof entry.run === 'function');
210
+ if (!command) return false;
211
+ return command.run({ ...ctx, key: undefined }) === true;
212
+ }
@@ -0,0 +1,103 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: comparing with another text
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Shows the document against another version of itself, inline: what the other
6
+ // version has and the document does not is drawn above the lines that replaced
7
+ // it, changed lines are marked, long unchanged stretches are folded away, and
8
+ // each difference can be put back to the other version (a button: the mouse
9
+ // and the keyboard both press it). The document stays editable; while it is
10
+ // read-only (a save or a reload is under way) the buttons do nothing, as the
11
+ // keys do. It is CodeMirror's unified merge view (@codemirror/merge, a chunk
12
+ // of the vendored bundle that is fetched the first time a comparison is
13
+ // asked for); its colours are the theme's (--ed-add / --ed-del).
14
+ //
15
+ // The panel compares a buffer with the file on disk; the same extension
16
+ // compares a file with its committed version. Takes the vendored bundle and
17
+ // the strings as arguments: no imports.
18
+
19
+ /** Lines of unchanged text kept around a difference, and the shortest stretch that is folded away. */
20
+ export const COMPARE_CONTEXT = Object.freeze({ margin: 3, minSize: 6 });
21
+
22
+ /**
23
+ * @param {object} cm the vendored CodeMirror bundle (editor-loader.js)
24
+ * @param {{ t: (key: string, params?: object) => string }} o
25
+ */
26
+ export function editorCompare(cm, { t }) {
27
+ let merge = null; // @codemirror/merge, once loaded
28
+
29
+ // The button that puts one difference back to the other version. The merge view also offers to accept a
30
+ // difference (to stop showing it); that is not a thing a comparison needs, so it gets no button.
31
+ const control = (label) => (type, action) => {
32
+ const node = document.createElement(type === 'reject' ? 'button' : 'span');
33
+ if (type !== 'reject') { node.hidden = true; return node; }
34
+ node.type = 'button';
35
+ node.className = 'fe-editor-compare-revert';
36
+ node.textContent = label;
37
+ // Pressed with the mouse, the keyboard stays in the text (the button is gone once it has acted).
38
+ node.addEventListener('mousedown', (event) => event.preventDefault());
39
+ // A click is what a mouse and a keyboard (Enter, Space) both end in. A document that cannot be edited right
40
+ // now (it is being saved or reloaded) is not edited through this button either.
41
+ node.addEventListener('click', (event) => {
42
+ const root = node.closest('.cm-editor');
43
+ const view = root ? cm.EditorView.findFromDOM(root) : null;
44
+ if (!view || view.state.readOnly) { event.preventDefault(); return; }
45
+ action(event);
46
+ view.focus();
47
+ });
48
+ return node;
49
+ };
50
+
51
+ return {
52
+ /**
53
+ * The extension that shows `original` against the document it is added to (the panel puts it in the view's
54
+ * comparison compartment). Rejects when the merge chunk cannot be loaded.
55
+ * @param {{ original: string, revertLabel: string, gutter?: boolean }} o `original`: the other version, line
56
+ * breaks as "\n"; `revertLabel`: what the button that puts a difference back says; `gutter`: false when
57
+ * another column already marks the changed lines (the git markers, for a comparison with the last commit)
58
+ */
59
+ async extension({ original, revertLabel, gutter = true }) {
60
+ if (!merge) merge = await cm.loadMerge();
61
+ // The lines that are new as a whole: every character of them is changed text. The theme gives those the
62
+ // line's tint only; a line that was modified keeps the stronger tint on the words that changed.
63
+ const { Decoration, EditorView, RangeSetBuilder, StateField } = cm;
64
+ const whole = Decoration.line({ class: 'cm-addedWhole' });
65
+ const wholeLines = (state) => {
66
+ const builder = new RangeSetBuilder();
67
+ for (const chunk of merge.getChunks(state)?.chunks || []) {
68
+ if (chunk.fromB >= chunk.toB) continue;
69
+ const end = Math.min(state.doc.length, chunk.endB);
70
+ for (let pos = chunk.fromB; pos <= end;) {
71
+ const line = state.doc.lineAt(pos);
72
+ if (line.length && chunk.changes.some((change) => chunk.fromB + change.fromB <= line.from && chunk.fromB + change.toB >= line.to)) builder.add(line.from, line.from, whole);
73
+ pos = line.to + 1;
74
+ }
75
+ }
76
+ return builder.finish();
77
+ };
78
+ const addedWhole = StateField.define({
79
+ create: wholeLines,
80
+ update: (value, tr) => (tr.docChanged || merge.getChunks(tr.state)?.chunks !== merge.getChunks(tr.startState)?.chunks ? wholeLines(tr.state) : value),
81
+ provide: (field) => EditorView.decorations.from(field),
82
+ });
83
+ return [
84
+ addedWhole,
85
+ merge.unifiedMergeView({
86
+ original,
87
+ highlightChanges: true,
88
+ gutter,
89
+ // What the other version had is drawn in the diff's own colour, not as code.
90
+ syntaxHighlightDeletions: false,
91
+ mergeControls: control(revertLabel),
92
+ collapseUnchanged: { ...COMPARE_CONTEXT },
93
+ }),
94
+ cm.EditorState.phrases.of({ '$ unchanged lines': t('editor.compare.unchanged', { count: '$' }) }),
95
+ ];
96
+ },
97
+
98
+ /** How many differences a state that is being compared shows; 0 for one that is not. */
99
+ differences(state) {
100
+ return merge?.getChunks(state)?.chunks.length ?? 0;
101
+ },
102
+ };
103
+ }
@@ -0,0 +1,172 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: the completion list
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What the list under the caret offers (CodeMirror's autocompletion): the
6
+ // completions of the document's language, and the words of the document. Three
7
+ // rules are kept here, on top of what CodeMirror does:
8
+ //
9
+ // - the list never offers the word that already stands before the caret: an
10
+ // option that would only write that word again is left out, and the list
11
+ // closes when nothing else is left to offer;
12
+ // - no name is listed twice: what the language offers (a variable) is not
13
+ // repeated as a word of the document, nor by a second source of the language;
14
+ // - while AI autocomplete is expected to suggest for the document, its words
15
+ // do not open the list by themselves (`wordsYield`). Asked for (Ctrl-Space),
16
+ // they are listed; what the language offers opens the list as always.
17
+ //
18
+ // An option that carries more than its label (a snippet, an `apply` of its own)
19
+ // is not "the same word": the two `for` loops of JavaScript stay next to the
20
+ // keyword, also when `for` is typed in full.
21
+ //
22
+ // CodeMirror asks a source once and reuses its answer while the word grows, so
23
+ // each answer is given an `update` of its own here: it is filtered again on
24
+ // every key. Takes the vendored bundle as an argument: no imports. The helpers
25
+ // at the top need no page: `node --test` loads them as they are.
26
+
27
+ /** How many completion sources a language (and the languages nested in it) is given room for at one position. */
28
+ export const LANGUAGE_SOURCES = 8;
29
+
30
+ /** An option that would only write its own label: there is nothing else to tell it from another of that label. */
31
+ export function isPlainOption(option) {
32
+ return !!option && typeof option.label === 'string' && (option.apply == null || option.apply === option.label);
33
+ }
34
+
35
+ /**
36
+ * The options of one source as the list shows them.
37
+ * @param {object[]} options a source's own (CodeMirror completions: `{ label, apply?, type?, detail? }`)
38
+ * @param {{ typed?: string, taken?: Set<string> | null }} [o] `typed`: the word before the caret, as far as this
39
+ * source would replace it; `taken`: the labels another source already offers as plain options
40
+ * @returns {object[]} in the same order, without the plain option that is the typed word, without a plain option
41
+ * whose label is taken, and with one plain option for a label at most
42
+ */
43
+ export function offeredOptions(options, { typed = '', taken = null } = {}) {
44
+ const seen = new Set();
45
+ const out = [];
46
+ for (const option of Array.isArray(options) ? options : []) {
47
+ if (!option || typeof option.label !== 'string') continue;
48
+ if (isPlainOption(option)) {
49
+ if (option.label === typed || taken?.has(option.label) || seen.has(option.label)) continue;
50
+ seen.add(option.label);
51
+ }
52
+ out.push(option);
53
+ }
54
+ return out;
55
+ }
56
+
57
+ /** The labels of a result's plain options, added to `into`. */
58
+ export function plainLabels(result, into = new Set()) {
59
+ for (const option of result?.options || []) if (isPlainOption(option)) into.add(option.label);
60
+ return into;
61
+ }
62
+
63
+ /**
64
+ * Whether a source's result still applies to the text between `from` and `to`, by the rule the result came with
65
+ * (CodeMirror's `validFor`: a function, or an expression the whole text must match). Without a rule it does not.
66
+ */
67
+ export function stillValid(validFor, state, from, to) {
68
+ if (!validFor) return false;
69
+ const text = state.sliceDoc(from, to);
70
+ if (typeof validFor === 'function') return !!validFor(text, from, to, state);
71
+ const { source } = validFor;
72
+ const start = source[0] !== '^';
73
+ const end = source[source.length - 1] !== '$';
74
+ const whole = start || end ? new RegExp(`${start ? '^' : ''}(?:${source})${end ? '$' : ''}`, validFor.flags ?? '') : validFor;
75
+ return whole.test(text);
76
+ }
77
+
78
+ /**
79
+ * @param {object} cm the vendored CodeMirror bundle (editor-loader.js)
80
+ * @param {{ wordsYield?: (context: object) => boolean }} [o] `wordsYield`: suggestions are expected at this caret,
81
+ * so the document's words stay out of a list nobody asked for
82
+ */
83
+ export function editorCompletionList(cm, { wordsYield = () => false } = {}) {
84
+ // A language may hand over a list of completions in place of a source: one source per list, as CodeMirror keeps them.
85
+ const fromLists = new WeakMap();
86
+ const asSource = (source) => {
87
+ if (typeof source === 'function') return source;
88
+ if (!Array.isArray(source)) return () => null;
89
+ if (!fromLists.has(source)) fromLists.set(source, cm.completeFromList(source));
90
+ return fromLists.get(source);
91
+ };
92
+ /** What the language at a position completes with: its own sources, in the order it names them. */
93
+ const languageSources = (state, pos) => state.languageDataAt('autocomplete', pos).map(asSource);
94
+
95
+ const isPromise = (value) => !!value && typeof value.then === 'function';
96
+
97
+ /**
98
+ * A source's result as the list uses it: its options filtered for the word before the caret, and filtered again
99
+ * each time that word changes.
100
+ * @param {object} original what the source answered
101
+ * @param {string} typed the text the result would replace
102
+ * @param {Set<string>} taken plain labels the sources in front of this one offer
103
+ */
104
+ function shape(original, typed, taken) {
105
+ const shaped = {
106
+ ...original,
107
+ options: offeredOptions(original.options, { typed, taken }),
108
+ // CodeMirror reuses a result while `validFor` holds, without asking anybody: here it asks `update` instead.
109
+ validFor: undefined,
110
+ update(current, from, to, context) {
111
+ const text = context.state.sliceDoc(from, to);
112
+ if (stillValid(original.validFor, context.state, from, to)) return shape({ ...original, from, to }, text, taken);
113
+ const renewed = typeof original.update === 'function' ? original.update(original, from, to, context) : null;
114
+ return renewed ? shape(renewed, context.state.sliceDoc(renewed.from, renewed.to ?? context.pos), taken) : null;
115
+ },
116
+ };
117
+ if (typeof original.map === 'function') {
118
+ shaped.map = (current, changes) => {
119
+ const mapped = original.map(original, changes);
120
+ return mapped ? shape(mapped, typed, taken) : null;
121
+ };
122
+ }
123
+ return shaped;
124
+ }
125
+
126
+ /**
127
+ * The result of the last of `sources`, shaped: without what the sources before it already offer.
128
+ * A source before it that fails or answers late is no reason to hold this one back: it offers nothing here.
129
+ */
130
+ function resultOf(sources, context) {
131
+ const last = sources.length - 1;
132
+ const before = sources.slice(0, last).map((source) => {
133
+ try {
134
+ const value = source(context);
135
+ return isPromise(value) ? value.catch(() => null) : value;
136
+ } catch { return null; }
137
+ });
138
+ let own;
139
+ try { own = sources[last](context); } catch (error) { return Promise.reject(error); } // CodeMirror logs it and closes the list
140
+ const finish = (result, earlier) => {
141
+ if (!result || !Array.isArray(result.options)) return null;
142
+ const taken = new Set();
143
+ for (const other of earlier) plainLabels(other, taken);
144
+ return shape(result, context.state.sliceDoc(result.from, result.to ?? context.pos), taken);
145
+ };
146
+ if (isPromise(own) || before.some(isPromise)) return Promise.all([own, ...before]).then(([result, ...earlier]) => finish(result, earlier));
147
+ return finish(own, before);
148
+ }
149
+
150
+ // One place for each source the language may have at the caret. CodeMirror keeps a source's answer by the
151
+ // source's identity: a place is always the same function, whichever source it stands for at a position.
152
+ const languagePlace = (index) => (context) => {
153
+ const sources = languageSources(context.state, context.pos);
154
+ return index < sources.length ? resultOf(sources.slice(0, index + 1), context) : null;
155
+ };
156
+
157
+ /** The words of the document, after everything the language offers. */
158
+ const words = (context) => {
159
+ if (!context.explicit && wordsYield(context)) return null;
160
+ const sources = languageSources(context.state, context.pos).slice(0, LANGUAGE_SOURCES);
161
+ return resultOf([...sources, cm.completeAnyWord], context);
162
+ };
163
+
164
+ const sources = [...Array.from({ length: LANGUAGE_SOURCES }, (_, index) => languagePlace(index)), words];
165
+
166
+ return {
167
+ /** The list, for the view: CodeMirror's autocompletion over the sources above. */
168
+ extension: cm.autocompletion({ override: sources }),
169
+ /** The sources themselves, in the order the list asks them (tests). */
170
+ sources,
171
+ };
172
+ }