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.
- package/CHANGELOG.md +57 -0
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +46 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +2 -2
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +20 -0
- package/hooks/claude-code/stop.mjs +5 -5
- package/lib/start-launcher.js +88 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/neural-interface/i18n/en.json +1500 -153
- package/neural-interface/i18n/pt-BR.json +1498 -152
- package/neural-interface/lib/assistant-api.js +62 -6
- package/neural-interface/lib/assistant-brains/claude.js +4 -0
- package/neural-interface/lib/assistant-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +31 -2
- package/neural-interface/lib/assistant-dispatch.js +62 -13
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +6 -2
- package/neural-interface/lib/assistant-pricing.js +21 -6
- package/neural-interface/lib/assistant-runtime.js +181 -12
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +181 -0
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-agent-bridge.js +33 -6
- package/neural-interface/lib/claude-model-catalog.js +30 -2
- package/neural-interface/lib/claude-temporary.js +46 -7
- package/neural-interface/lib/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- package/neural-interface/lib/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -0
- package/neural-interface/lib/git-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +202 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +10 -5
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- package/neural-interface/lib/request-gate.js +589 -0
- package/neural-interface/lib/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -0
- package/neural-interface/package-lock.json +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +3 -2
- package/neural-interface/public/onboarding.html +12 -1
- package/neural-interface/public/shared/agent-runtime-options.js +22 -6
- package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +18 -58
- package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
- package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
- package/neural-interface/public/shared/assistant/asst-render.js +162 -19
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +23 -329
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-state.js +185 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +158 -4
- package/neural-interface/public/shared/blob-store.js +111 -22
- package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
- package/neural-interface/public/shared/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +15 -0
- package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +160 -165
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +29 -1
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -1
- package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +18 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/styles.css +1414 -721
- package/neural-interface/public/shared/synabun-mascot.js +20 -56
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +157 -16
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +159 -15
- package/neural-interface/public/shared/ui-menubar.js +12 -30
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +45 -21
- package/neural-interface/public/shared/ui-settings.js +455 -488
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sync.js +16 -0
- package/neural-interface/public/shared/ui-terminal.js +23 -5
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- package/neural-interface/public/shared/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/worker-end.js +251 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +873 -1028
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +2 -8
- package/setup.js +44 -7
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- 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
|
+
}
|