synabun 2.0.0 → 2.0.7

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 (507) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/NOTICE +5 -0
  3. package/README.md +2 -1
  4. package/THIRD-PARTY-LICENSES.md +59 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +153 -18
  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 +26 -3
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/child-path.js +303 -0
  14. package/lib/claude-hooks.js +2 -1
  15. package/lib/data-home-migration.js +2 -0
  16. package/lib/desktop-app.js +265 -0
  17. package/lib/external-tools.js +7 -3
  18. package/lib/log-redact.js +102 -0
  19. package/lib/packaged-runtime.js +138 -11
  20. package/lib/start-launcher.js +88 -1
  21. package/mcp-server/dist/http.d.ts +3 -1
  22. package/mcp-server/dist/http.d.ts.map +1 -1
  23. package/mcp-server/dist/http.js +15 -4
  24. package/mcp-server/dist/http.js.map +1 -1
  25. package/mcp-server/dist/index.d.ts.map +1 -1
  26. package/mcp-server/dist/index.js +9 -6
  27. package/mcp-server/dist/index.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  33. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  34. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  35. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  36. package/mcp-server/dist/services/identity.d.ts +11 -0
  37. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  38. package/mcp-server/dist/services/identity.js +24 -0
  39. package/mcp-server/dist/services/identity.js.map +1 -1
  40. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  41. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  42. package/mcp-server/dist/services/neural-interface.js +34 -2
  43. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  44. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  45. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  46. package/mcp-server/dist/services/typesafe-config.js +4 -1
  47. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  48. package/mcp-server/dist/tools/agents.d.ts +10 -7
  49. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  50. package/mcp-server/dist/tools/agents.js +34 -13
  51. package/mcp-server/dist/tools/agents.js.map +1 -1
  52. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  53. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  54. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  55. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  56. package/mcp-server/dist/tui.js +2 -1
  57. package/mcp-server/dist/tui.js.map +1 -1
  58. package/mcp-server/dist/version.d.ts +5 -0
  59. package/mcp-server/dist/version.d.ts.map +1 -0
  60. package/mcp-server/dist/version.js +21 -0
  61. package/mcp-server/dist/version.js.map +1 -0
  62. package/mcp-server/package-lock.json +2 -2
  63. package/mcp-server/package.json +1 -3
  64. package/mcp-server/run.mjs +15 -9
  65. package/mcp-server/src/http.ts +18 -3
  66. package/mcp-server/src/index.ts +9 -6
  67. package/mcp-server/src/services/budget-judgments.ts +183 -0
  68. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  69. package/mcp-server/src/services/identity.ts +27 -0
  70. package/mcp-server/src/services/neural-interface.ts +34 -2
  71. package/mcp-server/src/services/typesafe-config.ts +4 -1
  72. package/mcp-server/src/tools/agents.ts +33 -14
  73. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  74. package/mcp-server/src/tui.ts +2 -1
  75. package/mcp-server/src/version.ts +22 -0
  76. package/neural-interface/i18n/en.json +2126 -209
  77. package/neural-interface/i18n/pt-BR.json +2123 -207
  78. package/neural-interface/lib/account-usage.js +163 -0
  79. package/neural-interface/lib/assistant-api.js +316 -57
  80. package/neural-interface/lib/assistant-brains/claude.js +19 -2
  81. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  82. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  83. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  84. package/neural-interface/lib/assistant-budget-split.js +408 -0
  85. package/neural-interface/lib/assistant-budget.js +94 -95
  86. package/neural-interface/lib/assistant-catalog.js +179 -27
  87. package/neural-interface/lib/assistant-config.js +91 -14
  88. package/neural-interface/lib/assistant-dispatch.js +494 -138
  89. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  90. package/neural-interface/lib/assistant-jev.js +26 -3
  91. package/neural-interface/lib/assistant-memory.js +24 -10
  92. package/neural-interface/lib/assistant-persona.js +73 -12
  93. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  94. package/neural-interface/lib/assistant-pricing.js +21 -6
  95. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  96. package/neural-interface/lib/assistant-route-gate.js +19 -3
  97. package/neural-interface/lib/assistant-router.js +373 -42
  98. package/neural-interface/lib/assistant-runtime.js +488 -59
  99. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  100. package/neural-interface/lib/assistant-test-density.js +142 -0
  101. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  102. package/neural-interface/lib/assistant-user-context.js +908 -0
  103. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  104. package/neural-interface/lib/assistant-user-learn.js +404 -0
  105. package/neural-interface/lib/assistant-user-threads.js +226 -0
  106. package/neural-interface/lib/assistant-user-writer.js +183 -0
  107. package/neural-interface/lib/augmented-path.js +317 -53
  108. package/neural-interface/lib/browser-background-tab.js +134 -0
  109. package/neural-interface/lib/browser-screenshot.js +678 -61
  110. package/neural-interface/lib/claude-accounts.js +342 -31
  111. package/neural-interface/lib/claude-agent-bridge.js +123 -9
  112. package/neural-interface/lib/claude-executable.js +6 -1
  113. package/neural-interface/lib/claude-model-catalog.js +33 -5
  114. package/neural-interface/lib/claude-panel-auth.js +221 -0
  115. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  116. package/neural-interface/lib/claude-temporary.js +46 -7
  117. package/neural-interface/lib/cli-install-api.js +137 -0
  118. package/neural-interface/lib/cli-install.js +797 -0
  119. package/neural-interface/lib/cli-path.js +130 -0
  120. package/neural-interface/lib/cli-resolver.js +744 -0
  121. package/neural-interface/lib/cli-update-planner.js +143 -7
  122. package/neural-interface/lib/cloudflared/install.js +533 -0
  123. package/neural-interface/lib/cloudflared/manager.js +545 -0
  124. package/neural-interface/lib/cloudflared/pins.js +115 -0
  125. package/neural-interface/lib/cloudflared/pins.json +63 -0
  126. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  127. package/neural-interface/lib/codex-account-auth.js +178 -0
  128. package/neural-interface/lib/codex-accounts.js +489 -0
  129. package/neural-interface/lib/codex-capabilities.js +2 -1
  130. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  131. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  132. package/neural-interface/lib/codex-panel-auth.js +62 -0
  133. package/neural-interface/lib/codex-session-settings.js +1 -1
  134. package/neural-interface/lib/codex-settings-schema.js +120 -0
  135. package/neural-interface/lib/desktop/config.js +3 -0
  136. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  137. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  138. package/neural-interface/lib/editor-completion.js +714 -0
  139. package/neural-interface/lib/editor-config.js +149 -0
  140. package/neural-interface/lib/editor-files-api.js +268 -0
  141. package/neural-interface/lib/editor-git-api.js +134 -0
  142. package/neural-interface/lib/file-explorer-api.js +1039 -0
  143. package/neural-interface/lib/file-explorer-system.js +355 -0
  144. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  145. package/neural-interface/lib/file-explorer-watch.js +239 -0
  146. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  147. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  148. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  149. package/neural-interface/lib/git-bash/install.js +529 -0
  150. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  151. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  152. package/neural-interface/lib/git-bash/manager.js +268 -0
  153. package/neural-interface/lib/git-bash/pins.js +206 -0
  154. package/neural-interface/lib/git-bash/pins.json +34 -0
  155. package/neural-interface/lib/git-bash/resolver.js +194 -0
  156. package/neural-interface/lib/git-diff-api.js +103 -0
  157. package/neural-interface/lib/git-read.js +234 -0
  158. package/neural-interface/lib/greeting/api.js +89 -0
  159. package/neural-interface/lib/greeting/budgets.js +90 -0
  160. package/neural-interface/lib/greeting/engine.js +461 -0
  161. package/neural-interface/lib/greeting/facts.js +320 -0
  162. package/neural-interface/lib/greeting/sources.js +252 -0
  163. package/neural-interface/lib/greeting/writer.js +139 -0
  164. package/neural-interface/lib/http-guards.js +189 -13
  165. package/neural-interface/lib/install-details.js +145 -0
  166. package/neural-interface/lib/machine-names.js +88 -0
  167. package/neural-interface/lib/mcp-installer.js +14 -2
  168. package/neural-interface/lib/memory-api.js +31 -7
  169. package/neural-interface/lib/native-loop-providers.js +26 -8
  170. package/neural-interface/lib/native-loop-runtime.js +9 -4
  171. package/neural-interface/lib/os-path.js +319 -0
  172. package/neural-interface/lib/plugin-installer.js +4 -2
  173. package/neural-interface/lib/project-storage.js +5 -7
  174. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  175. package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
  176. package/neural-interface/lib/pty-host/core.js +2 -1
  177. package/neural-interface/lib/pty-host/manager.js +27 -2
  178. package/neural-interface/lib/remote-policy.js +20 -11
  179. package/neural-interface/lib/request-gate.js +589 -0
  180. package/neural-interface/lib/session-title-generator.js +4 -3
  181. package/neural-interface/lib/system-about.js +340 -0
  182. package/neural-interface/lib/system-activity.js +66 -0
  183. package/neural-interface/lib/terminal-env.js +11 -3
  184. package/neural-interface/lib/terminal-path.js +55 -0
  185. package/neural-interface/lib/ui-blob-api.js +108 -0
  186. package/neural-interface/lib/ui-blob-store.js +68 -0
  187. package/neural-interface/lib/virtual-socket.js +12 -1
  188. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  189. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  190. package/neural-interface/lib/whatsapp/commands.js +13 -14
  191. package/neural-interface/lib/whatsapp/config.js +134 -20
  192. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  193. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  194. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  195. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  196. package/neural-interface/lib/whatsapp/manager.js +92 -32
  197. package/neural-interface/lib/whatsapp/paths.js +11 -1
  198. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  199. package/neural-interface/lib/whatsapp/redact.js +63 -0
  200. package/neural-interface/lib/whatsapp/service.js +343 -91
  201. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  202. package/neural-interface/package-lock.json +701 -2
  203. package/neural-interface/package.json +13 -2
  204. package/neural-interface/public/index.html +2 -0
  205. package/neural-interface/public/index2d.html +2 -0
  206. package/neural-interface/public/install-app.html +9 -6
  207. package/neural-interface/public/offline.html +19 -3
  208. package/neural-interface/public/onboarding.html +107 -2
  209. package/neural-interface/public/shared/account-i18n.js +7 -0
  210. package/neural-interface/public/shared/account-menu.js +539 -0
  211. package/neural-interface/public/shared/account-usage.js +19 -0
  212. package/neural-interface/public/shared/account-view.js +335 -0
  213. package/neural-interface/public/shared/agent-runtime-options.js +50 -14
  214. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  215. package/neural-interface/public/shared/assistant/asst-api.js +32 -2
  216. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  217. package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
  218. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  219. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  220. package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
  221. package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
  222. package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
  223. package/neural-interface/public/shared/assistant/asst-render.js +197 -23
  224. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  225. package/neural-interface/public/shared/assistant/asst-route.js +95 -343
  226. package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
  227. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  228. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  229. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  230. package/neural-interface/public/shared/assistant/asst-state.js +186 -2
  231. package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
  232. package/neural-interface/public/shared/blob-store.js +111 -22
  233. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  234. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  235. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  236. package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
  237. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  238. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  239. package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
  240. package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
  241. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  242. package/neural-interface/public/shared/claude-account-view.js +3 -0
  243. package/neural-interface/public/shared/cli-install-view.js +392 -0
  244. package/neural-interface/public/shared/cli-status.js +282 -22
  245. package/neural-interface/public/shared/constants.js +6 -1
  246. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  247. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  248. package/neural-interface/public/shared/cp/cp-styles.js +61 -0
  249. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  250. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  251. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  252. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  253. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  254. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  255. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  256. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  257. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  258. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  259. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  260. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  261. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  262. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  263. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  264. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  265. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  266. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  267. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  268. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  269. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  270. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  271. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  272. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  273. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  274. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  275. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  276. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  277. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  278. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  279. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  280. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  281. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  282. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  283. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  284. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  285. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  286. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  287. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  288. package/neural-interface/public/shared/file-links.js +339 -0
  289. package/neural-interface/public/shared/git-bash-view.js +145 -0
  290. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  291. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  292. package/neural-interface/public/shared/html-shell.js +161 -165
  293. package/neural-interface/public/shared/i18n.js +19 -1
  294. package/neural-interface/public/shared/install-details-view.js +86 -0
  295. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  296. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  297. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
  298. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  299. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
  300. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
  301. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  302. package/neural-interface/public/shared/pwa-install-page.js +32 -2
  303. package/neural-interface/public/shared/pwa-install.css +3 -3
  304. package/neural-interface/public/shared/pwa-install.js +100 -1
  305. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  306. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  307. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  308. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  309. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  310. package/neural-interface/public/shared/settings/settings-ia.js +33 -4
  311. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  312. package/neural-interface/public/shared/showcase-privacy.js +489 -0
  313. package/neural-interface/public/shared/start-bridge.js +238 -19
  314. package/neural-interface/public/shared/storage.js +30 -3
  315. package/neural-interface/public/shared/styles.css +1710 -770
  316. package/neural-interface/public/shared/synabun-mascot.js +68 -58
  317. package/neural-interface/public/shared/tunnel-view.js +167 -0
  318. package/neural-interface/public/shared/ui-claude-panel.js +369 -229
  319. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  320. package/neural-interface/public/shared/ui-detail.js +15 -0
  321. package/neural-interface/public/shared/ui-explorer.js +22 -0
  322. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  323. package/neural-interface/public/shared/ui-file-links.js +103 -0
  324. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  325. package/neural-interface/public/shared/ui-help.js +26 -0
  326. package/neural-interface/public/shared/ui-invite.js +126 -116
  327. package/neural-interface/public/shared/ui-judgments.js +10 -1
  328. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  329. package/neural-interface/public/shared/ui-loading.js +187 -19
  330. package/neural-interface/public/shared/ui-menubar.js +25 -33
  331. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  332. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  333. package/neural-interface/public/shared/ui-navbar.js +48 -22
  334. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  335. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  336. package/neural-interface/public/shared/ui-sessions.js +9 -3
  337. package/neural-interface/public/shared/ui-settings.js +509 -490
  338. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  339. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  340. package/neural-interface/public/shared/ui-sync.js +22 -0
  341. package/neural-interface/public/shared/ui-terminal.js +40 -5
  342. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  343. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  344. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  345. package/neural-interface/public/shared/ui-update.js +594 -5
  346. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  347. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  348. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  349. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  350. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  351. package/neural-interface/public/shared/worker-end.js +253 -0
  352. package/neural-interface/public/sw.js +2 -2
  353. package/neural-interface/public/variant/2d/main.js +10 -1
  354. package/neural-interface/public/variant/3d/main.js +10 -1
  355. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  356. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  357. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  358. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  359. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  360. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  361. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  362. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  363. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  364. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  365. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  366. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  367. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  368. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  369. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  370. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  371. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  372. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  373. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  374. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  375. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  376. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  377. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  378. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  379. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  380. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  381. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  382. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  383. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  384. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  385. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  386. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  387. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  388. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  389. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  390. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  391. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  392. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  393. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  394. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  395. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  396. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  397. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  398. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  399. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  400. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  401. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  402. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  403. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  404. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  405. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  406. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  407. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  408. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  409. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  410. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  411. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  412. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  413. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  414. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  415. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  416. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  417. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  418. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  419. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  420. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  421. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  422. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  423. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  424. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  425. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  426. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  427. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  428. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  429. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  430. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  431. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  432. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  433. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  434. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  435. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  436. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  437. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  438. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  439. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  440. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  441. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  442. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  443. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  444. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  445. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  446. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  447. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  448. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  449. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  450. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  451. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  452. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  453. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  454. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  455. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  456. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  457. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  458. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  459. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  460. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  461. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  462. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  463. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  464. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  465. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  466. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  467. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  468. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  469. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  470. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  471. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  472. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  473. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  474. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  475. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  476. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  477. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  478. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  479. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  480. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  481. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  482. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  483. package/neural-interface/scripts/build-vendor.mjs +78 -2
  484. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  485. package/neural-interface/server.js +2085 -1629
  486. package/neural-interface/templates/rulesets/README.md +1 -1
  487. package/package.json +5 -8
  488. package/setup.js +53 -15
  489. package/benchmarks/browser-live-results.json +0 -79
  490. package/benchmarks/browser-v2-results.json +0 -258
  491. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  492. package/neural-interface/public/category-logos/claude.svg +0 -1
  493. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  494. package/neural-interface/public/category-logos/openai.svg +0 -1
  495. package/neural-interface/public/category-logos/synabun.png +0 -0
  496. package/neural-interface/public/claude.svg +0 -1
  497. package/neural-interface/public/gallery.svg +0 -6
  498. package/neural-interface/public/leonardoai.svg +0 -1
  499. package/neural-interface/public/mcp.svg +0 -4
  500. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  501. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  502. package/neural-interface/public/sessionicon.svg +0 -8
  503. package/neural-interface/public/toolbar-album.svg +0 -8
  504. package/neural-interface/public/toolbar-question.svg +0 -6
  505. package/neural-interface/public/toolbar-sessions.svg +0 -6
  506. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  507. package/skills/synabun/synabunicon.png +0 -0
@@ -0,0 +1,229 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: problems in the file
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // A JSON file that does not parse gets the place of the error underlined, a
6
+ // mark in the gutter and the parser's message (on hover, and with F8). The
7
+ // check is CodeMirror's lint (@codemirror/lint, a chunk of the vendored bundle
8
+ // fetched with the first JSON file) run with the JSON language's own linter.
9
+ // Files that are JSON with comments by convention (tsconfig.json, .vscode/…)
10
+ // are checked with comments and trailing commas allowed; files that are not
11
+ // one JSON value (JSON Lines, JSON5) are not checked.
12
+ // Takes the vendored bundle as an argument: no imports. The helpers at the top
13
+ // need no page: `node --test` loads them as they are.
14
+
15
+ /** The check runs this long after the last edit. */
16
+ export const LINT_DELAY_MS = 400;
17
+
18
+ // JSON with comments, by the name tools gave the file.
19
+ const JSONC_NAMES = /^(tsconfig|jsconfig)(\..+)?\.json$|^(\.?devcontainer|\.eslintrc|\.babelrc|deno|turbo|\.luarc|api-extractor|typedoc)\.json$|^(\.babelrc|\.eslintrc|\.prettierrc|\.stylelintrc|\.swcrc|bun\.lock)$/;
20
+
21
+ /**
22
+ * Which check a file gets.
23
+ * @param {string} path the file's path or name
24
+ * @param {string} languageName the language it is shown in, as the status bar names it
25
+ * @returns {'json' | 'jsonc' | null} `json`: strict; `jsonc`: comments and trailing commas are allowed; null: none
26
+ */
27
+ export function lintKind(path, languageName) {
28
+ if (languageName !== 'JSON') return null;
29
+ const full = String(path ?? '').replace(/\\/g, '/');
30
+ const name = full.slice(full.lastIndexOf('/') + 1).toLowerCase();
31
+ const ext = name.lastIndexOf('.') > 0 ? name.slice(name.lastIndexOf('.') + 1) : '';
32
+ if (ext === 'jsonl' || ext === 'ndjson' || ext === 'json5') return null; // several values, or another syntax
33
+ if (ext === 'jsonc' || ext === 'code-workspace' || /\/\.vscode\/[^/]+$/.test(full) || JSONC_NAMES.test(name)) return 'jsonc';
34
+ return 'json';
35
+ }
36
+
37
+ /**
38
+ * `text` with its comments and trailing commas turned into spaces, every other character where it was: what is
39
+ * left is JSON when the text was JSON with comments, and an error in it is at the same place in the text.
40
+ */
41
+ export function blankJsonComments(text) {
42
+ const s = String(text ?? '');
43
+ const n = s.length;
44
+ const out = new Uint16Array(n);
45
+ let comma = -1; // a comma that nothing but blanks and comments has followed yet
46
+ let i = 0;
47
+ while (i < n) {
48
+ const c = s.charCodeAt(i);
49
+ if (c === 34) { // a string: copied as it is, to its closing quote or the end of its line
50
+ out[i++] = c;
51
+ while (i < n) {
52
+ const d = s.charCodeAt(i);
53
+ out[i++] = d;
54
+ if (d === 92 && i < n) out[i] = s.charCodeAt(i++);
55
+ else if (d === 34 || d === 10) break;
56
+ }
57
+ comma = -1;
58
+ continue;
59
+ }
60
+ if (c === 47 && s.charCodeAt(i + 1) === 47) {
61
+ while (i < n && s.charCodeAt(i) !== 10) out[i++] = 32;
62
+ continue;
63
+ }
64
+ if (c === 47 && s.charCodeAt(i + 1) === 42) {
65
+ const end = s.indexOf('*/', i + 2);
66
+ const stop = end === -1 ? n : end + 2;
67
+ for (; i < stop; i++) out[i] = s.charCodeAt(i) === 10 ? 10 : 32;
68
+ continue;
69
+ }
70
+ out[i] = c;
71
+ if (c === 44) comma = i;
72
+ else if (c === 125 || c === 93) { if (comma >= 0) out[comma] = 32; comma = -1; }
73
+ else if (c !== 32 && c !== 9 && c !== 10 && c !== 13) comma = -1;
74
+ i++;
75
+ }
76
+ let result = '';
77
+ for (let at = 0; at < n; at += 8192) result += String.fromCharCode.apply(null, out.subarray(at, Math.min(n, at + 8192)));
78
+ return result;
79
+ }
80
+
81
+ /**
82
+ * Where a parser's message says the error is, as an offset into the text. Null when the message names no place
83
+ * (some engines' do not).
84
+ */
85
+ export function parseErrorOffset(message, text) {
86
+ const words = String(message ?? '');
87
+ const s = String(text ?? '');
88
+ let m = /\bat position (\d+)/.exec(words);
89
+ if (m) return Math.min(Number(m[1]), s.length);
90
+ m = /\bat line (\d+) column (\d+)/.exec(words);
91
+ if (m) {
92
+ let at = 0;
93
+ for (let line = 1; line < Number(m[1]); line++) {
94
+ const next = s.indexOf('\n', at);
95
+ if (next === -1) return s.length;
96
+ at = next + 1;
97
+ }
98
+ return Math.min(at + Math.max(0, Number(m[2]) - 1), s.length);
99
+ }
100
+ if (/end of (JSON )?(input|data)/i.test(words)) return s.length;
101
+ return null;
102
+ }
103
+
104
+ const isWordChar = (c) => (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95 || c === 36 || c === 45 || c === 43 || c === 46 || c > 127;
105
+ const isBlank = (c) => c === 32 || c === 9 || c === 10 || c === 13;
106
+
107
+ /**
108
+ * The stretch of text to underline for an error at `offset`: the word there, or the one character; at a blank
109
+ * or past the end, the last thing before it. Never empty unless the text is, and never across a line break.
110
+ * @returns {{ from: number, to: number }}
111
+ */
112
+ export function problemSpan(text, offset) {
113
+ const s = String(text ?? '');
114
+ if (!s.length) return { from: 0, to: 0 };
115
+ let at = Math.max(0, Math.min(Number(offset) || 0, s.length - 1));
116
+ while (at > 0 && isBlank(s.charCodeAt(at))) at--;
117
+ if (isBlank(s.charCodeAt(at))) return { from: at, to: at + 1 === s.length || s.charCodeAt(at) === 10 ? at : at + 1 };
118
+ if (!isWordChar(s.charCodeAt(at))) return { from: at, to: at + 1 };
119
+ let from = at; let to = at + 1;
120
+ while (from > 0 && isWordChar(s.charCodeAt(from - 1))) from--;
121
+ while (to < s.length && isWordChar(s.charCodeAt(to))) to++;
122
+ return { from, to };
123
+ }
124
+
125
+ /**
126
+ * The problems of a JSON text.
127
+ * @param {string} text
128
+ * @param {{ relaxed?: boolean }} [o] `relaxed`: comments and trailing commas are not problems
129
+ * @returns {{ from: number, to: number, message: string, placed: boolean }[]} none, or the first one the parser
130
+ * met; `placed` is false when the parser's message named no place (`from` and `to` are then 0)
131
+ */
132
+ export function jsonProblems(text, { relaxed = false } = {}) {
133
+ const s = String(text ?? '');
134
+ if (!s.trim()) return []; // an empty file is not a broken one
135
+ let checked = s;
136
+ try { JSON.parse(s); return []; } catch (strict) {
137
+ let error = strict;
138
+ if (relaxed) {
139
+ checked = blankJsonComments(s);
140
+ try { JSON.parse(checked); return []; } catch (inner) { error = inner; }
141
+ }
142
+ if (!(error instanceof SyntaxError)) throw error;
143
+ const offset = parseErrorOffset(error.message, checked);
144
+ if (offset === null) return [{ from: 0, to: 0, message: error.message, placed: false }];
145
+ return [{ ...problemSpan(s, offset), message: error.message, placed: true }];
146
+ }
147
+ }
148
+
149
+ /**
150
+ * @param {object} cm the vendored CodeMirror bundle (editor-loader.js)
151
+ */
152
+ export function editorLint(cm) {
153
+ let lint = null; // @codemirror/lint, once loaded
154
+ let strictLinter = null; // the JSON language's own linter
155
+ let loading = null;
156
+ const built = new Map(); // kind → its extension, the same one for every document
157
+
158
+ function load() {
159
+ if (!loading) {
160
+ loading = Promise.all([cm.loadLint(), typeof cm.loadJsonLinter === 'function' ? cm.loadJsonLinter() : null]).then(([module, jsonLinter]) => {
161
+ lint = module;
162
+ strictLinter = typeof jsonLinter === 'function' ? jsonLinter() : null;
163
+ });
164
+ loading.catch(() => { loading = null; });
165
+ }
166
+ return loading;
167
+ }
168
+
169
+ // A problem the parser's message did not place is put at the first node the editor's own parser could not read.
170
+ function firstErrorNode(state) {
171
+ let at = -1;
172
+ cm.syntaxTree(state).iterate({ enter: (node) => { if (at < 0 && node.type.isError) at = node.from; return at < 0; } });
173
+ return at;
174
+ }
175
+
176
+ function source(kind) {
177
+ return (view) => {
178
+ const text = view.state.doc.toString();
179
+ let found;
180
+ if (kind === 'json' && strictLinter) {
181
+ // The JSON language's own linter says whether the file parses and with which message; where the
182
+ // underline goes is worked out here, so it is a stretch of text instead of a point.
183
+ found = text.trim() ? strictLinter(view).map((d) => {
184
+ const offset = parseErrorOffset(d.message, text);
185
+ return offset === null ? { from: 0, to: 0, message: d.message, placed: false } : { ...problemSpan(text, offset), message: d.message, placed: true };
186
+ }) : [];
187
+ } else {
188
+ found = jsonProblems(text, { relaxed: kind === 'jsonc' });
189
+ }
190
+ return found.map((problem) => {
191
+ let { from, to } = problem;
192
+ if (!problem.placed) {
193
+ const at = firstErrorNode(view.state);
194
+ ({ from, to } = problemSpan(text, at < 0 ? 0 : at));
195
+ }
196
+ return { from, to, severity: 'error', message: problem.message };
197
+ });
198
+ };
199
+ }
200
+
201
+ function build(kind) {
202
+ if (!built.has(kind)) built.set(kind, [lint.linter(source(kind), { delay: LINT_DELAY_MS }), lint.lintGutter()]);
203
+ return built.get(kind);
204
+ }
205
+
206
+ return {
207
+ /** The extension that checks a document of this kind, when the lint chunk is already there; else null. */
208
+ ready: (kind) => (lint && kind ? build(kind) : null),
209
+
210
+ /** The same, fetching the chunk first. Resolves to null for no kind; rejects when the chunk cannot be loaded. */
211
+ async extension(kind) {
212
+ if (!kind) return null;
213
+ await load();
214
+ return build(kind);
215
+ },
216
+
217
+ /** Go to the next / the previous problem. False when there is none (or nothing is checked). */
218
+ next: (view) => (lint ? lint.nextDiagnostic(view) : false),
219
+ previous: (view) => (lint ? lint.previousDiagnostic(view) : false),
220
+
221
+ /** How many problems a state shows. */
222
+ count: (state) => (lint ? lint.diagnosticCount(state) : 0),
223
+
224
+ /** Call `use(from, to)` for each problem of a state (the minimap draws them). */
225
+ each(state, use) {
226
+ if (lint) lint.forEachDiagnostic(state, (_diagnostic, from, to) => use(from, to));
227
+ },
228
+ };
229
+ }
@@ -0,0 +1,51 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: loading CodeMirror
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // CodeMirror is vendored (public/vendor/codemirror/, built by
6
+ // scripts/build-vendor.mjs): nothing comes from a CDN. The manifest names the
7
+ // entry file of the current build; it is imported once per page, the first
8
+ // time a file is opened. A failed load is reported to the caller, which shows
9
+ // it with a Retry; the next call tries again.
10
+
11
+ export const CODEMIRROR_BASE = '/vendor/codemirror/';
12
+ const ENTRY_NAME = /^codemirror-[A-Za-z0-9._-]+\.min\.js$/;
13
+
14
+ let _loading = null;
15
+ let _attempt = 0;
16
+
17
+ /**
18
+ * The vendored CodeMirror bundle.
19
+ * @param {{ fetchImpl?: typeof fetch, importModule?: (url: string) => Promise<object> }} [o] for tests
20
+ * @returns {Promise<{ cm: object, manifest: { version: string, entry: string, packages: object } }>}
21
+ * Rejects with an Error when the manifest or the bundle cannot be loaded.
22
+ */
23
+ export function loadCodeMirror({ fetchImpl = (...args) => fetch(...args), importModule = (url) => import(url) } = {}) {
24
+ if (_loading) return _loading;
25
+ const attempt = ++_attempt;
26
+ const loading = (async () => {
27
+ const res = await fetchImpl(`${CODEMIRROR_BASE}manifest.json`, { cache: 'no-cache' });
28
+ if (!res.ok) throw new Error(`CodeMirror manifest: HTTP ${res.status}`);
29
+ const manifest = await res.json();
30
+ if (typeof manifest?.entry !== 'string' || !ENTRY_NAME.test(manifest.entry)) {
31
+ throw new Error('CodeMirror manifest names no entry file');
32
+ }
33
+ // A module that failed to load can stay failed under its address for the rest of the page:
34
+ // a retry asks for it under another one.
35
+ const retry = attempt > 1 ? `?retry=${attempt}` : '';
36
+ const cm = await importModule(`${CODEMIRROR_BASE}${manifest.entry}${retry}`);
37
+ if (typeof cm?.EditorView !== 'function' || typeof cm?.EditorState !== 'function') {
38
+ throw new Error('CodeMirror bundle is incomplete');
39
+ }
40
+ return { cm, manifest };
41
+ })();
42
+ _loading = loading;
43
+ loading.catch(() => { if (_loading === loading) _loading = null; });
44
+ return loading;
45
+ }
46
+
47
+ /** Forget a load (tests). */
48
+ export function resetCodeMirrorLoader() {
49
+ _loading = null;
50
+ _attempt = 0;
51
+ }
@@ -0,0 +1,90 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: Markdown preview
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Markdown → the HTML the editor's Preview shows. The text is a file from disk,
6
+ // so it is untrusted: every character that means something in HTML is escaped
7
+ // before any tag is written, and the only attribute that carries a value from
8
+ // the text, a link's href, is limited to http:, https:, mailto: and relative
9
+ // targets. No DOM and no imports: loaded by `node --test` as it is.
10
+
11
+ const ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
12
+
13
+ /** `text` with & < > " ' as HTML entities: safe as element content and inside a quoted attribute. */
14
+ export function escapeHtml(text) {
15
+ return String(text ?? '').replace(/[&<>"']/g, (c) => ESCAPES[c]);
16
+ }
17
+
18
+ // What a browser drops or ignores when it reads a URL's scheme: C0 controls, spaces of every kind, and the
19
+ // invisible format characters. Without them "java\tscript:" is seen for what it is.
20
+ const URL_NOISE = /[\u0000-\u0020\u007F-\u00A0\u1680\u2000-\u200F\u2028-\u202F\u205F-\u2064\u3000\uFEFF]/g;
21
+ const ALLOWED_SCHEMES = new Set(['http', 'https', 'mailto']);
22
+
23
+ /**
24
+ * A link target that is safe to open, or null. Relative targets (a path, `#anchor`, `?query`, `//host/path`)
25
+ * pass; a target with a scheme passes only for http, https and mailto.
26
+ * @param {string} target as written in the Markdown, or already HTML-escaped (escaping adds no scheme)
27
+ */
28
+ export function safeLinkTarget(target) {
29
+ const raw = String(target ?? '').trim();
30
+ if (!raw) return null;
31
+ const scheme = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(raw.replace(URL_NOISE, ''));
32
+ if (scheme && !ALLOWED_SCHEMES.has(scheme[1].toLowerCase())) return null;
33
+ return raw;
34
+ }
35
+
36
+ const HOLD = '\u0000';
37
+
38
+ /**
39
+ * Render Markdown for the Preview pane: headings, emphasis, inline and fenced code, links, block quotes,
40
+ * tables, lists and paragraphs. Front matter is left out. Raw HTML in the text is shown as text.
41
+ * @returns {string} HTML
42
+ */
43
+ export function renderMarkdown(markdown) {
44
+ let text = String(markdown ?? '').replace(/\r\n?/g, '\n').split(HOLD).join('');
45
+ text = text.replace(/^---\n[\s\S]*?\n---\n?/, '');
46
+ text = escapeHtml(text);
47
+
48
+ // Code and links are set aside while the rest is formatted, so nothing inside them is read as Markdown and
49
+ // nothing is formatted into an attribute.
50
+ const held = [];
51
+ const hold = (html) => `${HOLD}${held.push(html) - 1}${HOLD}`;
52
+ text = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => hold(`<pre><code class="lang-${lang}">${code.trimEnd()}</code></pre>`));
53
+ text = text.replace(/`([^`\n]+)`/g, (_, code) => hold(`<code>${code}</code>`));
54
+ text = text.replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, (whole, label, target) => {
55
+ if (target.includes(HOLD)) return whole;
56
+ const href = safeLinkTarget(target);
57
+ return href === null ? label : `${hold(`<a href="${href}" target="_blank" rel="noopener noreferrer">`)}${label}${hold('</a>')}`;
58
+ });
59
+
60
+ text = text.replace(/^######\s+(.+)$/gm, '<h6>$1</h6>');
61
+ text = text.replace(/^#####\s+(.+)$/gm, '<h5>$1</h5>');
62
+ text = text.replace(/^####\s+(.+)$/gm, '<h4>$1</h4>');
63
+ text = text.replace(/^###\s+(.+)$/gm, '<h3>$1</h3>');
64
+ text = text.replace(/^##\s+(.+)$/gm, '<h2>$1</h2>');
65
+ text = text.replace(/^#\s+(.+)$/gm, '<h1>$1</h1>');
66
+ text = text.replace(/^---+$/gm, '<hr>');
67
+ text = text.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
68
+ text = text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
69
+ text = text.replace(/\*(.+?)\*/g, '<em>$1</em>');
70
+ text = text.replace(/~~(.+?)~~/g, '<del>$1</del>');
71
+ text = text.replace(/^&gt;\s?(.*)$/gm, '<blockquote>$1</blockquote>');
72
+ text = text.replace(/<\/blockquote>\n<blockquote>/g, '\n');
73
+ text = text.replace(/^(\|.+\|)\n(\|[-| :]+\|)\n((?:\|.+\|\n?)+)/gm, (_, header, _sep, body) => {
74
+ const ths = header.split('|').filter((c) => c.trim()).map((c) => `<th>${c.trim()}</th>`).join('');
75
+ const rows = body.trim().split('\n').map((row) => {
76
+ const tds = row.split('|').filter((c) => c.trim()).map((c) => `<td>${c.trim()}</td>`).join('');
77
+ return `<tr>${tds}</tr>`;
78
+ }).join('');
79
+ return `<table><thead><tr>${ths}</tr></thead><tbody>${rows}</tbody></table>`;
80
+ });
81
+ text = text.replace(/^[-*]\s+(.+)$/gm, '<li>$1</li>');
82
+ text = text.replace(/((?:<li>.*<\/li>\n?)+)/g, '<ul>$1</ul>');
83
+ text = text.replace(/^\d+\.\s+(.+)$/gm, '<oli>$1</oli>');
84
+ text = text.replace(/((?:<oli>.*<\/oli>\n?)+)/g, (match) => `<ol>${match.replace(/<\/?oli>/g, (tag) => tag.replace('oli', 'li'))}</ol>`);
85
+ // A line that does not start with a tag (or with code or a link, which are held) becomes a paragraph.
86
+ text = text.replace(/^(?![<\u0000])(.+)$/gm, '<p>$1</p>');
87
+ text = text.replace(/<p>\s*<\/p>/g, '');
88
+
89
+ return text.replace(new RegExp(`${HOLD}(\\d+)${HOLD}`, 'g'), (_, i) => held[Number(i)]);
90
+ }
@@ -0,0 +1,233 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Code editor: menus
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The one popup of the editor panel: a tab's menu, the status bar's lists
6
+ // (language, indentation, line ending), the View menu, the command palette and
7
+ // go-to-file. A list of actions, optionally under a search field; arrows,
8
+ // Home / End, Enter and Escape work it, and its keys never reach the app's
9
+ // shortcuts. They are taken at the window, before anything in the page sees
10
+ // them: other parts of the app listen for keys at the document in the capture
11
+ // phase (the whiteboard takes Escape there whenever the keyboard is not in a
12
+ // text field), and a listener on the menu itself would come after those.
13
+ // One is open at a time; closed with the keyboard, it gives the keyboard back
14
+ // to where it was. Its look is `.fe-editor-menu` in styles.css (the menu
15
+ // treatment of DESIGN.md).
16
+ // No imports: the caller hands in the words.
17
+
18
+ let _current = null;
19
+ const KEY_EVENTS = ['keydown', 'keyup', 'keypress'];
20
+
21
+ /** Close the open menu, if there is one. */
22
+ export function closeEditorMenu() {
23
+ _current?.close();
24
+ }
25
+
26
+ /**
27
+ * @typedef {object} EditorMenuItem
28
+ * @property {string} [id]
29
+ * @property {string} [label]
30
+ * @property {string} [hint] muted words after the label
31
+ * @property {boolean} [checked] the one in force (shown with a check)
32
+ * @property {boolean} [toggle] `checked` is a switch of its own, not one choice among the others
33
+ * @property {boolean} [disabled]
34
+ * @property {boolean} [separator] a line between groups; takes no other field
35
+ */
36
+
37
+ /**
38
+ * Open a menu.
39
+ * @param {object} o
40
+ * @param {HTMLElement} o.host the element it is added to (the editor panel: its variables and its focus ring apply)
41
+ * @param {HTMLElement} [o.anchor] the control it belongs to: it opens against it and gives the keyboard back to it
42
+ * @param {{ x: number, y: number }} [o.at] or a point of the page (a right click): the keyboard then goes back to
43
+ * whatever had it when the menu opened
44
+ * @param {string} o.label its accessible name
45
+ * @param {EditorMenuItem[]} o.items
46
+ * @param {{ label: string, placeholder: string, empty: string | (() => string), filter: (query: string) => EditorMenuItem[] }} [o.search]
47
+ * a field above the list: `filter` gives the items for what was typed; `empty` is said when there are none
48
+ * @param {string} [o.className] a class of its own, next to `fe-editor-menu` (the palette is wider)
49
+ * @param {string} [o.activeId] the item the keyboard starts on (else the checked one, else the first)
50
+ * @param {(id: string) => void} o.onSelect
51
+ * @param {() => void} [o.onClose]
52
+ * @returns {{ close: (o?: { refocus?: boolean }) => void, setItems: (items: EditorMenuItem[]) => void, element: HTMLElement }}
53
+ * `setItems`: show other items in the open menu (a search whose answer arrived)
54
+ */
55
+ export function openEditorMenu({ host, anchor = null, at = null, label, items, search = null, className = '', activeId = null, onSelect, onClose = () => {} }) {
56
+ closeEditorMenu();
57
+
58
+ const menu = document.createElement('div');
59
+ menu.className = className ? `fe-editor-menu ${className}` : 'fe-editor-menu';
60
+ menu.setAttribute('role', search ? 'dialog' : 'menu');
61
+ menu.setAttribute('aria-label', label);
62
+ menu.tabIndex = -1;
63
+ const list = document.createElement('div');
64
+ list.className = 'fe-editor-menu-list';
65
+ list.id = 'fe-editor-menu-list';
66
+ list.setAttribute('role', search ? 'listbox' : 'none');
67
+ let input = null;
68
+ if (search) {
69
+ input = document.createElement('input');
70
+ input.type = 'text';
71
+ input.className = 'fe-editor-menu-search';
72
+ input.placeholder = search.placeholder;
73
+ input.spellcheck = false;
74
+ input.autocomplete = 'off';
75
+ input.setAttribute('role', 'combobox');
76
+ input.setAttribute('aria-label', search.label);
77
+ input.setAttribute('aria-expanded', 'true');
78
+ input.setAttribute('aria-controls', list.id);
79
+ input.setAttribute('aria-autocomplete', 'list');
80
+ menu.append(input);
81
+ }
82
+ menu.append(list);
83
+
84
+ let shown = []; // the items that can be chosen, in order
85
+ let activeAt = -1;
86
+ let open = true;
87
+ // What had the keyboard when the menu opened: where it goes back to, for a menu that belongs to no control.
88
+ const opener = document.activeElement instanceof HTMLElement && document.activeElement !== document.body ? document.activeElement : null;
89
+
90
+ function setActive(index, { scroll = true } = {}) {
91
+ activeAt = shown.length ? Math.max(0, Math.min(shown.length - 1, index)) : -1;
92
+ shown.forEach((entry, i) => entry.node.classList.toggle('active', i === activeAt));
93
+ const node = shown[activeAt]?.node || null;
94
+ (input || menu).setAttribute('aria-activedescendant', node ? node.id : '');
95
+ if (node && scroll) node.scrollIntoView({ block: 'nearest' });
96
+ }
97
+
98
+ function render(entries) {
99
+ list.textContent = '';
100
+ shown = [];
101
+ let seq = 0;
102
+ for (const item of entries) {
103
+ if (item.separator) {
104
+ const line = document.createElement('div');
105
+ line.className = 'fe-editor-menu-sep';
106
+ line.setAttribute('role', 'separator');
107
+ list.append(line);
108
+ continue;
109
+ }
110
+ const node = document.createElement('div');
111
+ node.className = 'fe-editor-menu-item';
112
+ node.id = `fe-editor-menu-item-${seq++}`;
113
+ node.dataset.id = item.id;
114
+ node.setAttribute('role', search ? 'option' : (item.checked === undefined ? 'menuitem' : (item.toggle ? 'menuitemcheckbox' : 'menuitemradio')));
115
+ if (item.checked !== undefined) node.setAttribute(search ? 'aria-selected' : 'aria-checked', String(!!item.checked));
116
+ if (item.disabled) node.setAttribute('aria-disabled', 'true');
117
+ const check = document.createElement('span');
118
+ check.className = 'fe-editor-menu-check';
119
+ check.setAttribute('aria-hidden', 'true');
120
+ if (item.checked) check.textContent = '✓';
121
+ const text = document.createElement('span');
122
+ text.className = 'fe-editor-menu-label';
123
+ text.textContent = item.label;
124
+ node.append(check, text);
125
+ if (item.hint) {
126
+ const hint = document.createElement('span');
127
+ hint.className = 'fe-editor-menu-hint';
128
+ hint.textContent = item.hint;
129
+ node.append(hint);
130
+ }
131
+ list.append(node);
132
+ if (!item.disabled) shown.push({ item, node });
133
+ }
134
+ if (!shown.length && search) {
135
+ const none = document.createElement('div');
136
+ none.className = 'fe-editor-menu-empty';
137
+ none.textContent = typeof search.empty === 'function' ? search.empty() : search.empty;
138
+ list.append(none);
139
+ }
140
+ const wanted = activeId === null ? -1 : shown.findIndex((entry) => entry.item.id === activeId);
141
+ const checked = shown.findIndex((entry) => entry.item.checked && !entry.item.toggle);
142
+ setActive(wanted >= 0 ? wanted : (checked >= 0 ? checked : 0));
143
+ }
144
+
145
+ function close({ refocus = false } = {}) {
146
+ if (!open) return;
147
+ open = false;
148
+ if (_current === handle) _current = null;
149
+ document.removeEventListener('mousedown', onOutside, true);
150
+ window.removeEventListener('resize', onResize);
151
+ for (const type of KEY_EVENTS) window.removeEventListener(type, onKey, true);
152
+ menu.remove();
153
+ anchor?.setAttribute('aria-expanded', 'false');
154
+ if (refocus) {
155
+ const back = anchor || (opener?.isConnected ? opener : null);
156
+ back?.focus();
157
+ }
158
+ onClose();
159
+ }
160
+
161
+ function choose(entry) {
162
+ if (!entry) return;
163
+ close({ refocus: true });
164
+ onSelect(entry.item.id);
165
+ }
166
+
167
+ const onOutside = (event) => { if (!menu.contains(event.target)) close(); };
168
+ const onResize = () => close();
169
+
170
+ menu.addEventListener('click', (event) => {
171
+ const node = event.target.closest?.('.fe-editor-menu-item');
172
+ if (node) choose(shown.find((entry) => entry.node === node));
173
+ });
174
+ menu.addEventListener('mousemove', (event) => {
175
+ const node = event.target.closest?.('.fe-editor-menu-item');
176
+ const index = node ? shown.findIndex((entry) => entry.node === node) : -1;
177
+ if (index >= 0 && index !== activeAt) setActive(index, { scroll: false });
178
+ });
179
+ // The keys pressed in the menu are its own. Taken at the window in the capture phase, so that they are the
180
+ // menu's before a listener at the document can take them for a shortcut of its own, and go no further: what a
181
+ // key types into the search field is not held back by that.
182
+ function onKey(event) {
183
+ if (!open || !menu.contains(event.target)) return;
184
+ event.stopPropagation();
185
+ if (event.type !== 'keydown') return;
186
+ switch (event.key) {
187
+ case 'ArrowDown': event.preventDefault(); setActive(activeAt + 1 >= shown.length ? 0 : activeAt + 1); break;
188
+ case 'ArrowUp': event.preventDefault(); setActive(activeAt - 1 < 0 ? shown.length - 1 : activeAt - 1); break;
189
+ case 'Home': if (!input) { event.preventDefault(); setActive(0); } break;
190
+ case 'End': if (!input) { event.preventDefault(); setActive(shown.length - 1); } break;
191
+ case 'PageDown': event.preventDefault(); setActive(activeAt + 8); break;
192
+ case 'PageUp': event.preventDefault(); setActive(activeAt - 8); break;
193
+ case 'Enter': if (!event.isComposing) { event.preventDefault(); choose(shown[activeAt]); } break;
194
+ case ' ': if (!input) { event.preventDefault(); choose(shown[activeAt]); } break;
195
+ case 'Escape': event.preventDefault(); close({ refocus: true }); break;
196
+ case 'Tab': event.preventDefault(); close({ refocus: true }); break;
197
+ default:
198
+ }
199
+ }
200
+ if (input) input.addEventListener('input', () => render(search.filter(input.value)));
201
+
202
+ render(search ? search.filter('') : items);
203
+ host.append(menu);
204
+
205
+ // Against the anchor (above it when there is no room below), or at the point; always inside the host.
206
+ const frame = host.getBoundingClientRect();
207
+ const size = menu.getBoundingClientRect();
208
+ const gap = 6;
209
+ let left; let top;
210
+ if (anchor) {
211
+ const box = anchor.getBoundingClientRect();
212
+ left = box.left;
213
+ top = box.bottom + gap + size.height <= frame.bottom ? box.bottom + gap : box.top - gap - size.height;
214
+ } else {
215
+ left = at?.x ?? frame.left;
216
+ top = at?.y ?? frame.top;
217
+ }
218
+ left = Math.max(frame.left + 8, Math.min(left, frame.right - size.width - 8));
219
+ top = Math.max(frame.top + 8, Math.min(top, frame.bottom - size.height - 8));
220
+ menu.style.left = `${Math.round(left - frame.left)}px`;
221
+ menu.style.top = `${Math.round(top - frame.top)}px`;
222
+
223
+ anchor?.setAttribute('aria-expanded', 'true');
224
+ document.addEventListener('mousedown', onOutside, true);
225
+ window.addEventListener('resize', onResize);
226
+ for (const type of KEY_EVENTS) window.addEventListener(type, onKey, true);
227
+ (input || menu).focus();
228
+ setActive(activeAt);
229
+
230
+ const handle = { close, setItems: (entries) => { if (open) render(entries); }, element: menu };
231
+ _current = handle;
232
+ return handle;
233
+ }