synabun 2.0.0-beta → 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 +65 -0
- package/NOTICE +5 -0
- package/README.md +1 -1
- package/THIRD-PARTY-LICENSES.md +51 -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/launcher.mjs +40 -0
- package/lib/claude-hooks.js +61 -8
- package/lib/client-config-repair.js +4 -3
- package/lib/data-home-migration.js +4 -0
- package/lib/desktop-pwa.js +206 -0
- package/lib/external-tools.js +221 -0
- package/lib/first-launch-protection.js +5 -0
- package/lib/packaged-runtime.js +110 -0
- package/lib/start-launcher.js +1396 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -3
- package/neural-interface/i18n/en.json +1540 -154
- package/neural-interface/i18n/pt-BR.json +1538 -153
- 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-envelope.js +9 -6
- 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/augmented-path.js +23 -3
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-language.js +83 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-agent-bridge.js +87 -153
- package/neural-interface/lib/claude-executable.js +241 -0
- package/neural-interface/lib/claude-model-catalog.js +82 -93
- 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-binary-runtime.js +69 -337
- package/neural-interface/lib/native-loop-providers.js +40 -49
- package/neural-interface/lib/offline-page.js +54 -0
- 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/session-title-generator.js +5 -0
- package/neural-interface/lib/synabun-update-plan.js +19 -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 -253
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/install-app.html +34 -0
- package/neural-interface/public/manifest.json +2 -0
- package/neural-interface/public/offline.html +402 -66
- package/neural-interface/public/onboarding.html +264 -147
- 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 +26 -64
- 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-rack-styles.js +0 -3
- package/neural-interface/public/shared/assistant/asst-render.js +219 -67
- 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 +162 -6
- 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/command-runner-model.js +30 -0
- 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 +169 -170
- 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 +62 -0
- package/neural-interface/public/shared/pwa-install.css +12 -0
- package/neural-interface/public/shared/pwa-install.js +127 -0
- 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-rules.js +160 -0
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/start-bridge.js +802 -0
- package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
- package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
- package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
- package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
- package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
- package/neural-interface/public/shared/styles.css +2219 -1117
- package/neural-interface/public/shared/synabun-mascot.js +20 -56
- package/neural-interface/public/shared/term-ready.js +307 -0
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +160 -19
- package/neural-interface/public/shared/ui-command-runner.js +251 -468
- 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-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +367 -12
- package/neural-interface/public/shared/ui-menubar.js +21 -55
- 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 +491 -547
- 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 +153 -149
- 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 +3 -1
- package/neural-interface/public/variant/2d/main.js +2 -1
- package/neural-interface/public/variant/3d/main.js +2 -1
- 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/rebuild-pty.js +15 -12
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +1092 -1212
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +13 -7
- package/postinstall.js +11 -81
- package/setup.js +111 -6
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/docs/browser-v2.md +0 -109
- package/docs/hook-integrity.md +0 -89
- package/docs/judgments.md +0 -182
- package/docs/style-guide.md +0 -246
- 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/scripts/audit-claude-hook-usage.mjs +0 -12
- package/scripts/audit-memory-duplicates.mjs +0 -44
- package/scripts/measure-claude-hook-overhead.mjs +0 -77
- package/scripts/recover-user-config.mjs +0 -254
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Code editor: what a file is on disk
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// CodeMirror holds every line break as "\n". These helpers read what the file
|
|
6
|
+
// used (line ending, byte-order mark, indentation) when it is opened, and write
|
|
7
|
+
// the document back the same way when it is saved. No DOM and no imports: loaded
|
|
8
|
+
// by `node --test` as it is.
|
|
9
|
+
|
|
10
|
+
export const BOM = '\uFEFF';
|
|
11
|
+
const REPLACEMENT = '\uFFFD';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Take a leading byte-order mark off `content`.
|
|
15
|
+
* @param {string} content what the server sent
|
|
16
|
+
* @param {boolean} [reported] the server's `bom` field. A server that sends it has already taken the mark off;
|
|
17
|
+
* one that does not (it predates the field) sends the mark as the first character.
|
|
18
|
+
* @returns {{ text: string, bom: boolean }}
|
|
19
|
+
*/
|
|
20
|
+
export function splitBom(content, reported) {
|
|
21
|
+
const text = String(content ?? '');
|
|
22
|
+
if (typeof reported === 'boolean') return { text, bom: reported };
|
|
23
|
+
return text.charCodeAt(0) === 0xFEFF ? { text: text.slice(1), bom: true } : { text, bom: false };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The line ending a text uses. `eol` is the style it is saved in: the more common of LF and CRLF (LF when
|
|
28
|
+
* neither leads). `mixed` says the text holds more than one kind of line break, a lone CR included, so a save
|
|
29
|
+
* rewrites some of them.
|
|
30
|
+
* @returns {{ eol: 'LF' | 'CRLF', mixed: boolean, counts: { lf: number, crlf: number, cr: number } }}
|
|
31
|
+
*/
|
|
32
|
+
export function detectLineEnding(text) {
|
|
33
|
+
const s = String(text ?? '');
|
|
34
|
+
let lf = 0; let crlf = 0; let cr = 0;
|
|
35
|
+
for (let i = s.indexOf('\n'); i !== -1; i = s.indexOf('\n', i + 1)) {
|
|
36
|
+
if (i > 0 && s.charCodeAt(i - 1) === 13) crlf++;
|
|
37
|
+
else lf++;
|
|
38
|
+
}
|
|
39
|
+
for (let i = s.indexOf('\r'); i !== -1; i = s.indexOf('\r', i + 1)) {
|
|
40
|
+
if (s.charCodeAt(i + 1) !== 10) cr++;
|
|
41
|
+
}
|
|
42
|
+
const kinds = (lf ? 1 : 0) + (crlf ? 1 : 0) + (cr ? 1 : 0);
|
|
43
|
+
return { eol: crlf > lf ? 'CRLF' : 'LF', mixed: kinds > 1, counts: { lf, crlf, cr } };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const SPACE_STEPS = [2, 4, 8];
|
|
47
|
+
/** The width a tab is drawn at when nothing says otherwise. */
|
|
48
|
+
export const DEFAULT_TAB_WIDTH = 4;
|
|
49
|
+
/** What Tab inserts in a file that shows no indentation of its own. */
|
|
50
|
+
export const DEFAULT_SPACE_STEP = 2;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* How a text is indented, read from its leading whitespace: with tabs, or with the step of 2, 4 or 8 spaces
|
|
54
|
+
* that most often separates one line from the line before it.
|
|
55
|
+
* @param {string} text
|
|
56
|
+
* @param {{ maxLines?: number }} [o] lines read from the start (a large file is not read whole)
|
|
57
|
+
* @returns {{ kind: 'spaces' | 'tabs', size: number, detected: boolean }} `size` is the space step, or the
|
|
58
|
+
* default tab width for a file indented with tabs; `detected` is false when the text gave no evidence
|
|
59
|
+
*/
|
|
60
|
+
export function detectIndentation(text, { maxLines = 5000 } = {}) {
|
|
61
|
+
const s = String(text ?? '');
|
|
62
|
+
let tabLines = 0; let spaceLines = 0;
|
|
63
|
+
const steps = new Map(SPACE_STEPS.map((n) => [n, 0]));
|
|
64
|
+
let previous = 0; // leading spaces of the last line that had content and was not indented with a tab
|
|
65
|
+
let at = 0;
|
|
66
|
+
for (let n = 0; n < maxLines && at <= s.length; n++) {
|
|
67
|
+
let end = s.indexOf('\n', at);
|
|
68
|
+
if (end === -1) end = s.length;
|
|
69
|
+
let i = at;
|
|
70
|
+
let spaces = 0; let tab = false;
|
|
71
|
+
for (; i < end; i++) {
|
|
72
|
+
const c = s.charCodeAt(i);
|
|
73
|
+
if (c === 32) spaces++;
|
|
74
|
+
else if (c === 9) tab = true;
|
|
75
|
+
else break;
|
|
76
|
+
}
|
|
77
|
+
const blank = i >= end || s.charCodeAt(i) === 13;
|
|
78
|
+
if (!blank) {
|
|
79
|
+
if (tab) tabLines++;
|
|
80
|
+
else {
|
|
81
|
+
if (spaces) spaceLines++;
|
|
82
|
+
const step = Math.abs(spaces - previous);
|
|
83
|
+
if (steps.has(step)) steps.set(step, steps.get(step) + 1);
|
|
84
|
+
previous = spaces;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
at = end + 1;
|
|
88
|
+
}
|
|
89
|
+
if (tabLines > spaceLines) return { kind: 'tabs', size: DEFAULT_TAB_WIDTH, detected: true };
|
|
90
|
+
let best = 0; let bestCount = 0;
|
|
91
|
+
for (const n of SPACE_STEPS) {
|
|
92
|
+
if (steps.get(n) > bestCount) { best = n; bestCount = steps.get(n); }
|
|
93
|
+
}
|
|
94
|
+
if (!best) return { kind: 'spaces', size: DEFAULT_SPACE_STEP, detected: false };
|
|
95
|
+
return { kind: 'spaces', size: best, detected: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The indentation the editor uses for a file: what was detected, unless a preference says otherwise.
|
|
100
|
+
* @param {{ kind: 'spaces' | 'tabs', size: number }} detected from detectIndentation()
|
|
101
|
+
* @param {{ tabSize?: 'auto' | number, indentWith?: 'auto' | 'spaces' | 'tabs' }} [editing] the `editing` preferences
|
|
102
|
+
* @returns {{ kind: 'spaces' | 'tabs', tabSize: number, unit: string }} `unit` is what one level of indentation inserts
|
|
103
|
+
*/
|
|
104
|
+
export function resolveIndentation(detected, editing = {}) {
|
|
105
|
+
const found = detected && (detected.kind === 'tabs' || detected.kind === 'spaces')
|
|
106
|
+
? detected : { kind: 'spaces', size: DEFAULT_SPACE_STEP };
|
|
107
|
+
const kind = editing.indentWith === 'tabs' || editing.indentWith === 'spaces' ? editing.indentWith : found.kind;
|
|
108
|
+
const wanted = Number(editing.tabSize);
|
|
109
|
+
let tabSize;
|
|
110
|
+
if (editing.tabSize !== 'auto' && Number.isInteger(wanted) && wanted >= 1 && wanted <= 16) tabSize = wanted;
|
|
111
|
+
else if (kind === found.kind) tabSize = found.size;
|
|
112
|
+
else tabSize = kind === 'tabs' ? DEFAULT_TAB_WIDTH : DEFAULT_SPACE_STEP;
|
|
113
|
+
return { kind, tabSize, unit: kind === 'tabs' ? '\t' : ' '.repeat(tabSize) };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The indentation a file uses when the user picked one in the status bar: that choice, over what was detected
|
|
118
|
+
* and over the preferences. Without a choice it is resolveIndentation().
|
|
119
|
+
* @param {{ kind: 'spaces' | 'tabs', size: number }} detected from detectIndentation()
|
|
120
|
+
* @param {{ tabSize?: 'auto' | number, indentWith?: string }} [editing] the `editing` preferences
|
|
121
|
+
* @param {{ kind: 'spaces', size: number } | { kind: 'tabs' } | null} [choice]
|
|
122
|
+
* @returns {{ kind: 'spaces' | 'tabs', tabSize: number, unit: string }}
|
|
123
|
+
*/
|
|
124
|
+
export function chooseIndentation(detected, editing = {}, choice = null) {
|
|
125
|
+
if (!choice) return resolveIndentation(detected, editing);
|
|
126
|
+
if (choice.kind === 'tabs') {
|
|
127
|
+
// How wide a tab is drawn is not part of the choice: the preference, else what the file showed.
|
|
128
|
+
const wanted = Number(editing.tabSize);
|
|
129
|
+
const tabSize = editing.tabSize !== 'auto' && Number.isInteger(wanted) && wanted >= 1 && wanted <= 16
|
|
130
|
+
? wanted : (detected?.kind === 'tabs' ? detected.size : DEFAULT_TAB_WIDTH);
|
|
131
|
+
return { kind: 'tabs', tabSize, unit: '\t' };
|
|
132
|
+
}
|
|
133
|
+
const size = SPACE_STEPS.includes(choice.size) ? choice.size : DEFAULT_SPACE_STEP;
|
|
134
|
+
return { kind: 'spaces', tabSize: size, unit: ' '.repeat(size) };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The one change that turns `before` into `after`: what lies between the start and the end the two share.
|
|
139
|
+
* A file reloaded from disk is applied this way, so the caret, the view and the undo history outside the
|
|
140
|
+
* change stay where they are.
|
|
141
|
+
* @returns {{ from: number, to: number, insert: string } | null} null when the texts are the same
|
|
142
|
+
*/
|
|
143
|
+
export function minimalChange(before, after) {
|
|
144
|
+
const a = String(before ?? '');
|
|
145
|
+
const b = String(after ?? '');
|
|
146
|
+
if (a === b) return null;
|
|
147
|
+
const shortest = Math.min(a.length, b.length);
|
|
148
|
+
let start = 0;
|
|
149
|
+
while (start < shortest && a.charCodeAt(start) === b.charCodeAt(start)) start++;
|
|
150
|
+
let end = 0;
|
|
151
|
+
while (end < shortest - start && a.charCodeAt(a.length - 1 - end) === b.charCodeAt(b.length - 1 - end)) end++;
|
|
152
|
+
return { from: start, to: a.length - end, insert: b.slice(start, b.length - end) };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The text a save writes: `text` (line breaks as "\n", the way CodeMirror holds it) in the file's line ending,
|
|
157
|
+
* behind its byte-order mark when it had one.
|
|
158
|
+
*/
|
|
159
|
+
export function serializeDocument(text, { eol = 'LF', bom = false } = {}) {
|
|
160
|
+
const body = eol === 'CRLF' ? String(text ?? '').replace(/\n/g, '\r\n') : String(text ?? '');
|
|
161
|
+
return bom ? BOM + body : body;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Whether `content` lost bytes when it was decoded. A server that predates the encoding check decodes a file
|
|
166
|
+
* that is not UTF-8 anyway, with U+FFFD in place of each invalid sequence: the text then no longer encodes to
|
|
167
|
+
* the file's size, and saving it would write the damage to disk.
|
|
168
|
+
* @param {string} content the text as received, byte-order mark included
|
|
169
|
+
* @param {number} sizeOnDisk the size the server reported, in bytes
|
|
170
|
+
*/
|
|
171
|
+
export function looksLossyDecoded(content, sizeOnDisk) {
|
|
172
|
+
const s = String(content ?? '');
|
|
173
|
+
if (!Number.isFinite(sizeOnDisk) || !s.includes(REPLACEMENT)) return false;
|
|
174
|
+
return new TextEncoder().encode(s).length !== sizeOnDisk;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The document position of a 1-based line and column, held inside the document: a line past the end is the
|
|
179
|
+
* last line, a column past the end of its line is the end of that line.
|
|
180
|
+
* @param {{ lines: number, line: (n: number) => { from: number, length: number } }} doc a CodeMirror Text
|
|
181
|
+
* @returns {{ pos: number, line: number, column: number }}
|
|
182
|
+
*/
|
|
183
|
+
export function positionAt(doc, lineNumber, columnNumber = 1) {
|
|
184
|
+
const line = Math.min(Math.max(1, Math.floor(Number(lineNumber)) || 1), Math.max(1, doc.lines));
|
|
185
|
+
const info = doc.line(line);
|
|
186
|
+
const column = Math.min(Math.max(1, Math.floor(Number(columnNumber)) || 1), info.length + 1);
|
|
187
|
+
return { pos: info.from + column - 1, line, column };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* What a "go to line" field asks for: `12`, `12:8` or `12,8`.
|
|
192
|
+
* @returns {{ line: number, column: number } | null} null when it is not a line number
|
|
193
|
+
*/
|
|
194
|
+
export function parseLineTarget(input) {
|
|
195
|
+
const m = /^\s*(\d{1,9})\s*(?:[:,]\s*(\d{1,9})\s*)?$/.exec(String(input ?? ''));
|
|
196
|
+
if (!m) return null;
|
|
197
|
+
const line = Number(m[1]);
|
|
198
|
+
if (line < 1) return null;
|
|
199
|
+
return { line, column: m[2] ? Math.max(1, Number(m[2])) : 1 };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** Above this size the minimap, the git markers and the problem check are off for a document. */
|
|
203
|
+
export const LARGE_DOCUMENT_BYTES = 2 * 1024 * 1024;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Whether a document is too large for the minimap, the git markers and the problem check.
|
|
207
|
+
* @param {{ size?: number, length?: number }} o `size`: the file on disk, in bytes; `length`: the text in the editor
|
|
208
|
+
*/
|
|
209
|
+
export function isLargeDocument({ size = 0, length = 0 } = {}) {
|
|
210
|
+
return Math.max(Number(size) || 0, Number(length) || 0) > LARGE_DOCUMENT_BYTES;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* What a save changes in the text before it is written, for the two on-save preferences.
|
|
215
|
+
* trimTrailingWhitespace spaces and tabs at the end of a line go
|
|
216
|
+
* insertFinalNewline the text ends with exactly one line break: a missing one is added, empty lines after
|
|
217
|
+
* the last line with text go (with trimming on, a line of only spaces is an empty line)
|
|
218
|
+
* An empty text stays empty. With both off nothing changes.
|
|
219
|
+
* @param {string} text line breaks as "\n", the way CodeMirror holds it
|
|
220
|
+
* @param {{ trimTrailingWhitespace?: boolean, insertFinalNewline?: boolean }} [o]
|
|
221
|
+
* @returns {{ from: number, to: number, insert: string }[]} changes in document order that do not touch each other
|
|
222
|
+
*/
|
|
223
|
+
export function saveFixes(text, { trimTrailingWhitespace = false, insertFinalNewline = false } = {}) {
|
|
224
|
+
const s = String(text ?? '');
|
|
225
|
+
const changes = [];
|
|
226
|
+
if (!s || (!trimTrailingWhitespace && !insertFinalNewline)) return changes;
|
|
227
|
+
let contentEnd = s.length;
|
|
228
|
+
let ending = null;
|
|
229
|
+
if (insertFinalNewline) {
|
|
230
|
+
const blank = trimTrailingWhitespace ? (c) => c === 10 || c === 32 || c === 9 : (c) => c === 10;
|
|
231
|
+
while (contentEnd > 0 && blank(s.charCodeAt(contentEnd - 1))) contentEnd--;
|
|
232
|
+
const wanted = contentEnd === 0 ? '' : '\n';
|
|
233
|
+
if (s.slice(contentEnd) !== wanted) ending = { from: contentEnd, to: s.length, insert: wanted };
|
|
234
|
+
else contentEnd = s.length; // the end is as it should be: only lines are trimmed
|
|
235
|
+
}
|
|
236
|
+
if (trimTrailingWhitespace) {
|
|
237
|
+
const trailing = /[ \t]+$/gm;
|
|
238
|
+
for (let m = trailing.exec(s); m; m = trailing.exec(s)) {
|
|
239
|
+
if (ending && m.index >= ending.from) break; // what follows is replaced with the ending
|
|
240
|
+
changes.push({ from: m.index, to: m.index + m[0].length, insert: '' });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (ending) changes.push(ending);
|
|
244
|
+
return changes;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** `text` with the changes of saveFixes() (or any changes in document order) applied. */
|
|
248
|
+
export function applyChanges(text, changes) {
|
|
249
|
+
const s = String(text ?? '');
|
|
250
|
+
let out = '';
|
|
251
|
+
let at = 0;
|
|
252
|
+
for (const change of changes) {
|
|
253
|
+
out += s.slice(at, change.from) + change.insert;
|
|
254
|
+
at = change.to;
|
|
255
|
+
}
|
|
256
|
+
return out + s.slice(at);
|
|
257
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Code editor: which lines differ from the last commit
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// The decisions behind the git markers (editor-git.js draws them): what the
|
|
6
|
+
// server said about a file's committed version, which lines of the document
|
|
7
|
+
// differ from it, and what the gutter shows for them. A line comparison by the
|
|
8
|
+
// O(ND) method, bounded: beyond its limit the stretch that differs is marked as
|
|
9
|
+
// one change instead of being taken apart. No DOM and no imports: loaded by
|
|
10
|
+
// `node --test` as it is.
|
|
11
|
+
|
|
12
|
+
/** Edits a comparison takes apart; a stretch that needs more is one "modified" block. */
|
|
13
|
+
export const DIFF_MAX_EDITS = 1500;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* What GET /api/file-head answered.
|
|
17
|
+
* @param {{ status?: number, body?: object | null }} response
|
|
18
|
+
* @returns {{ tracked: boolean, text: string | null }} `text`: the committed version, line breaks as "\n"; null
|
|
19
|
+
* when git does not know the file, when what it holds is not text, or when the server could not say
|
|
20
|
+
*/
|
|
21
|
+
export function readHeadResponse({ status = 0, body = null } = {}) {
|
|
22
|
+
if (status !== 200 || body?.ok !== true || body.tracked !== true) return { tracked: false, text: null };
|
|
23
|
+
if (typeof body.content !== 'string') return { tracked: true, text: null };
|
|
24
|
+
return { tracked: true, text: body.content.replace(/\r\n?/g, '\n') };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The stretches where two lists of lines differ.
|
|
29
|
+
* @param {string[]} before the committed lines
|
|
30
|
+
* @param {string[]} after the document's lines
|
|
31
|
+
* @param {{ maxEdits?: number }} [o]
|
|
32
|
+
* @returns {{ aFrom: number, aTo: number, bFrom: number, bTo: number }[]} in order; each replaces lines
|
|
33
|
+
* `aFrom`..`aTo` of `before` with lines `bFrom`..`bTo` of `after` (0-based, the end not included)
|
|
34
|
+
*/
|
|
35
|
+
export function diffLines(before, after, { maxEdits = DIFF_MAX_EDITS } = {}) {
|
|
36
|
+
const a = before; const b = after;
|
|
37
|
+
let start = 0;
|
|
38
|
+
const shortest = Math.min(a.length, b.length);
|
|
39
|
+
while (start < shortest && a[start] === b[start]) start++;
|
|
40
|
+
let endA = a.length; let endB = b.length;
|
|
41
|
+
while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) { endA--; endB--; }
|
|
42
|
+
if (start === endA && start === endB) return [];
|
|
43
|
+
const whole = [{ aFrom: start, aTo: endA, bFrom: start, bTo: endB }];
|
|
44
|
+
if (start === endA || start === endB) return whole;
|
|
45
|
+
|
|
46
|
+
// The middle, each distinct line as a number: comparing lines is then comparing numbers.
|
|
47
|
+
const ids = new Map();
|
|
48
|
+
const intern = (line) => { let id = ids.get(line); if (id === undefined) { id = ids.size; ids.set(line, id); } return id; };
|
|
49
|
+
const n = endA - start; const m = endB - start;
|
|
50
|
+
const x0 = new Int32Array(n); const y0 = new Int32Array(m);
|
|
51
|
+
for (let i = 0; i < n; i++) x0[i] = intern(a[start + i]);
|
|
52
|
+
for (let i = 0; i < m; i++) y0[i] = intern(b[start + i]);
|
|
53
|
+
|
|
54
|
+
const max = Math.min(n + m, Math.max(1, maxEdits));
|
|
55
|
+
const offset = max + 1;
|
|
56
|
+
const v = new Int32Array(2 * max + 3);
|
|
57
|
+
const trace = [];
|
|
58
|
+
let found = -1;
|
|
59
|
+
for (let d = 0; d <= max && found < 0; d++) {
|
|
60
|
+
trace.push(v.slice(offset - d, offset + d + 1)); // the furthest points before this round, for the way back
|
|
61
|
+
for (let k = -d; k <= d; k += 2) {
|
|
62
|
+
let x = (k === -d || (k !== d && v[offset + k - 1] < v[offset + k + 1])) ? v[offset + k + 1] : v[offset + k - 1] + 1;
|
|
63
|
+
let y = x - k;
|
|
64
|
+
while (x < n && y < m && x0[x] === y0[y]) { x++; y++; }
|
|
65
|
+
v[offset + k] = x;
|
|
66
|
+
if (x >= n && y >= m) { found = d; break; }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (found < 0) return whole;
|
|
70
|
+
|
|
71
|
+
// Back from the end: the lines both have are what separates one stretch from the next.
|
|
72
|
+
const hunks = [];
|
|
73
|
+
let open = null;
|
|
74
|
+
const edit = (x, y, dx, dy) => {
|
|
75
|
+
if (open && open.aFrom === x + dx && open.bFrom === y + dy) { open.aFrom = x; open.bFrom = y; return; }
|
|
76
|
+
open = { aFrom: x, aTo: x + dx, bFrom: y, bTo: y + dy };
|
|
77
|
+
hunks.push(open);
|
|
78
|
+
};
|
|
79
|
+
let x = n; let y = m;
|
|
80
|
+
for (let d = found; d > 0; d--) {
|
|
81
|
+
const prev = trace[d];
|
|
82
|
+
const at = (k) => prev[k + d];
|
|
83
|
+
const k = x - y;
|
|
84
|
+
const down = k === -d || (k !== d && at(k - 1) < at(k + 1));
|
|
85
|
+
const prevK = down ? k + 1 : k - 1;
|
|
86
|
+
const prevX = at(prevK);
|
|
87
|
+
const prevY = prevX - prevK;
|
|
88
|
+
if (down) edit(prevX, prevY, 0, 1); // a line of `after` that `before` does not have
|
|
89
|
+
else edit(prevX, prevY, 1, 0); // a line of `before` that is gone
|
|
90
|
+
x = prevX; y = prevY;
|
|
91
|
+
}
|
|
92
|
+
return hunks.reverse().map((hunk) => ({ aFrom: hunk.aFrom + start, aTo: hunk.aTo + start, bFrom: hunk.bFrom + start, bTo: hunk.bTo + start }));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* What the gutter shows for the stretches diffLines() found.
|
|
97
|
+
* @param {{ aFrom: number, aTo: number, bFrom: number, bTo: number }[]} hunks
|
|
98
|
+
* @param {number} lineCount lines in the document
|
|
99
|
+
* @returns {{ from: number, to: number, kind: 'added' | 'modified' | 'deleted', after?: boolean }[]} `from`..`to`:
|
|
100
|
+
* lines of the document, 1-based, both included. `deleted` marks one line: the one committed lines were removed
|
|
101
|
+
* in front of, or (with `after`) the last line when they were removed behind it.
|
|
102
|
+
*/
|
|
103
|
+
export function lineMarks(hunks, lineCount) {
|
|
104
|
+
const marks = [];
|
|
105
|
+
const last = Math.max(1, lineCount);
|
|
106
|
+
for (const hunk of hunks) {
|
|
107
|
+
if (hunk.bTo > hunk.bFrom) {
|
|
108
|
+
marks.push({ from: hunk.bFrom + 1, to: hunk.bTo, kind: hunk.aTo > hunk.aFrom ? 'modified' : 'added' });
|
|
109
|
+
} else if (hunk.aTo > hunk.aFrom) {
|
|
110
|
+
const after = hunk.bFrom >= last;
|
|
111
|
+
marks.push({ from: after ? last : hunk.bFrom + 1, to: after ? last : hunk.bFrom + 1, kind: 'deleted', ...(after ? { after: true } : {}) });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return marks;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The marks for a document against its committed version.
|
|
119
|
+
* @param {string[]} headLines the committed lines
|
|
120
|
+
* @param {string[]} lines the document's lines
|
|
121
|
+
*/
|
|
122
|
+
export function gitMarks(headLines, lines, options) {
|
|
123
|
+
return lineMarks(diffLines(headLines, lines, options), lines.length);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** How many lines each kind of mark covers: `{ added, modified, deleted }` (`deleted` counts places, not lines). */
|
|
127
|
+
export function countMarks(marks) {
|
|
128
|
+
const count = { added: 0, modified: 0, deleted: 0 };
|
|
129
|
+
for (const mark of marks) count[mark.kind] += mark.kind === 'deleted' ? 1 : mark.to - mark.from + 1;
|
|
130
|
+
return count;
|
|
131
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Code editor: git markers
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// A thin column next to the fold arrows that says which lines differ from the
|
|
6
|
+
// last commit: added, modified, and where committed lines were removed. Which
|
|
7
|
+
// lines those are is decided in editor-git-logic.js; the panel fetches the
|
|
8
|
+
// committed text (GET /api/file-head), compares after edits and saves, and
|
|
9
|
+
// hands the result here as an effect. The marks are part of the document's
|
|
10
|
+
// state, so each tab keeps its own, and they move with the text until the next
|
|
11
|
+
// comparison. The Diff view of the file against the last commit is the inline
|
|
12
|
+
// comparison of editor-compare.js, fed the committed text.
|
|
13
|
+
// Takes the vendored bundle as an argument: no imports.
|
|
14
|
+
|
|
15
|
+
/** A comparison runs this long after the last edit. */
|
|
16
|
+
export const GIT_REFRESH_MS = 300;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {object} cm the vendored CodeMirror bundle (editor-loader.js)
|
|
20
|
+
*/
|
|
21
|
+
export function editorGit(cm) {
|
|
22
|
+
const { GutterMarker, RangeSet, RangeSetBuilder, StateEffect, StateField, gutter } = cm;
|
|
23
|
+
|
|
24
|
+
class ChangeMarker extends GutterMarker {
|
|
25
|
+
constructor(kind, after = false) {
|
|
26
|
+
super();
|
|
27
|
+
this.kind = kind;
|
|
28
|
+
this.after = after;
|
|
29
|
+
this.elementClass = `cm-gitLine cm-gitLine-${kind}${after ? ' cm-gitLine-after' : ''}`;
|
|
30
|
+
}
|
|
31
|
+
eq(other) { return other.kind === this.kind && other.after === this.after; }
|
|
32
|
+
}
|
|
33
|
+
const markers = {
|
|
34
|
+
added: new ChangeMarker('added'),
|
|
35
|
+
modified: new ChangeMarker('modified'),
|
|
36
|
+
deleted: new ChangeMarker('deleted'),
|
|
37
|
+
deletedAfter: new ChangeMarker('deleted', true),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const setMarks = StateEffect.define();
|
|
41
|
+
|
|
42
|
+
function build(doc, marks) {
|
|
43
|
+
const builder = new RangeSetBuilder();
|
|
44
|
+
for (const mark of marks) {
|
|
45
|
+
const marker = mark.kind === 'deleted' ? (mark.after ? markers.deletedAfter : markers.deleted) : markers[mark.kind];
|
|
46
|
+
if (!marker) continue;
|
|
47
|
+
const last = Math.min(mark.to, doc.lines);
|
|
48
|
+
for (let n = Math.max(1, mark.from); n <= last; n++) {
|
|
49
|
+
const at = doc.line(n).from;
|
|
50
|
+
builder.add(at, at, marker);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return builder.finish();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const field = StateField.define({
|
|
57
|
+
create: () => RangeSet.empty,
|
|
58
|
+
update(value, tr) {
|
|
59
|
+
for (const effect of tr.effects) if (effect.is(setMarks)) return build(tr.state.doc, effect.value);
|
|
60
|
+
return tr.docChanged ? value.map(tr.changes) : value;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const column = gutter({ class: 'cm-gitGutter', markers: (view) => view.state.field(field) });
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
/** The column and the marks it shows (none until an effect brings them). For the view's git compartment. */
|
|
68
|
+
extension: [field, column],
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Show these marks.
|
|
72
|
+
* @param {{ from: number, to: number, kind: 'added' | 'modified' | 'deleted', after?: boolean }[]} marks
|
|
73
|
+
* lines of the document (editor-git-logic.js: gitMarks)
|
|
74
|
+
*/
|
|
75
|
+
marks: (marks) => setMarks.of(marks || []),
|
|
76
|
+
|
|
77
|
+
/** Whether a state has the column. */
|
|
78
|
+
has: (state) => state.field(field, false) !== undefined,
|
|
79
|
+
|
|
80
|
+
/** The kinds of the marks a state shows, by line number: for a test, or a count. */
|
|
81
|
+
lines(state) {
|
|
82
|
+
const found = new Map();
|
|
83
|
+
const set = state.field(field, false);
|
|
84
|
+
if (!set) return found;
|
|
85
|
+
for (let cursor = set.iter(); cursor.value; cursor.next()) found.set(state.doc.lineAt(cursor.from).number, cursor.value.kind);
|
|
86
|
+
return found;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/** Call `use(position, kind)` for each mark between two document positions (the minimap draws them). */
|
|
90
|
+
between(state, from, to, use) {
|
|
91
|
+
const set = state.field(field, false);
|
|
92
|
+
if (set) set.between(from, to, (at, _end, marker) => { use(at, marker.kind); });
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Code editor: which language a file is
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// File name → the language it is highlighted as, and the word the status bar
|
|
6
|
+
// shows. A language is named the way @codemirror/language-data names it; the
|
|
7
|
+
// view looks the name up in the vendored bundle and loads its chunk. This table
|
|
8
|
+
// only holds what language-data does not know by itself: dotfiles, lockfiles
|
|
9
|
+
// and a few extensions. For every other name the view asks language-data.
|
|
10
|
+
// No DOM and no imports: loaded by `node --test` as it is.
|
|
11
|
+
|
|
12
|
+
const PROPERTIES = 'Properties files';
|
|
13
|
+
|
|
14
|
+
// Exact file names, lower case.
|
|
15
|
+
const BY_NAME = {
|
|
16
|
+
// shell start-up files
|
|
17
|
+
'.bashrc': { language: 'Shell' },
|
|
18
|
+
'.bash_profile': { language: 'Shell' },
|
|
19
|
+
'.bash_aliases': { language: 'Shell' },
|
|
20
|
+
'.bash_logout': { language: 'Shell' },
|
|
21
|
+
'.profile': { language: 'Shell' },
|
|
22
|
+
'.zshrc': { language: 'Shell' },
|
|
23
|
+
'.zshenv': { language: 'Shell' },
|
|
24
|
+
'.zprofile': { language: 'Shell' },
|
|
25
|
+
'.zlogin': { language: 'Shell' },
|
|
26
|
+
'.envrc': { language: 'Shell' },
|
|
27
|
+
// key = value files
|
|
28
|
+
'.npmrc': { language: PROPERTIES, label: 'Properties' },
|
|
29
|
+
'.yarnrc': { language: PROPERTIES, label: 'Properties' },
|
|
30
|
+
'.editorconfig': { language: PROPERTIES, label: 'EditorConfig' },
|
|
31
|
+
'.gitconfig': { language: PROPERTIES, label: 'Git Config' },
|
|
32
|
+
'.gitmodules': { language: PROPERTIES, label: 'Git Config' },
|
|
33
|
+
'.netrc': { language: null, label: 'Plain Text' },
|
|
34
|
+
// JSON under another name
|
|
35
|
+
'.babelrc': { language: 'JSON' },
|
|
36
|
+
'.eslintrc': { language: 'JSON' },
|
|
37
|
+
'.prettierrc': { language: 'JSON' },
|
|
38
|
+
'.stylelintrc': { language: 'JSON' },
|
|
39
|
+
'.swcrc': { language: 'JSON' },
|
|
40
|
+
'.watchmanconfig': { language: 'JSON' },
|
|
41
|
+
// lockfiles
|
|
42
|
+
'package-lock.json': { language: 'JSON' },
|
|
43
|
+
'npm-shrinkwrap.json': { language: 'JSON' },
|
|
44
|
+
'composer.lock': { language: 'JSON' },
|
|
45
|
+
'pipfile.lock': { language: 'JSON' },
|
|
46
|
+
'flake.lock': { language: 'JSON' },
|
|
47
|
+
'bun.lock': { language: 'JSON' },
|
|
48
|
+
'deno.lock': { language: 'JSON' },
|
|
49
|
+
'yarn.lock': { language: 'YAML' },
|
|
50
|
+
'pnpm-lock.yaml': { language: 'YAML' },
|
|
51
|
+
'cargo.lock': { language: 'TOML' },
|
|
52
|
+
'poetry.lock': { language: 'TOML' },
|
|
53
|
+
'uv.lock': { language: 'TOML' },
|
|
54
|
+
'pdm.lock': { language: 'TOML' },
|
|
55
|
+
'gemfile.lock': { language: null, label: 'Plain Text' },
|
|
56
|
+
'go.sum': { language: null, label: 'Plain Text' },
|
|
57
|
+
// build files
|
|
58
|
+
'makefile': { language: 'Shell', label: 'Makefile' },
|
|
59
|
+
'gnumakefile': { language: 'Shell', label: 'Makefile' },
|
|
60
|
+
'containerfile': { language: 'Dockerfile' },
|
|
61
|
+
'gemfile': { language: 'Ruby' },
|
|
62
|
+
'rakefile': { language: 'Ruby' },
|
|
63
|
+
'pipfile': { language: 'TOML' },
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// File name patterns, first match wins.
|
|
67
|
+
const BY_PATTERN = [
|
|
68
|
+
{ test: /^\.env(\.|$)/i, language: PROPERTIES, label: 'ENV' },
|
|
69
|
+
{ test: /\.env$/i, language: PROPERTIES, label: 'ENV' },
|
|
70
|
+
{ test: /^\.[\w-]*ignore$/i, language: 'Shell', label: 'Ignore' },
|
|
71
|
+
{ test: /^dockerfile(\.|$)/i, language: 'Dockerfile' },
|
|
72
|
+
{ test: /\.dockerfile$/i, language: 'Dockerfile' },
|
|
73
|
+
{ test: /^\.(eslintrc|prettierrc|stylelintrc)\.json[c5]?$/i, language: 'JSON' },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// Extensions language-data has no entry for, lower case.
|
|
77
|
+
const BY_EXTENSION = {
|
|
78
|
+
jsonc: { language: 'JSON' },
|
|
79
|
+
json5: { language: 'JSON' },
|
|
80
|
+
jsonl: { language: 'JSON' },
|
|
81
|
+
ndjson: { language: 'JSON' },
|
|
82
|
+
webmanifest: { language: 'JSON' },
|
|
83
|
+
mdx: { language: 'Markdown' },
|
|
84
|
+
mdc: { language: 'Markdown' },
|
|
85
|
+
svelte: { language: 'HTML', label: 'Svelte' },
|
|
86
|
+
astro: { language: 'HTML', label: 'Astro' },
|
|
87
|
+
xhtml: { language: 'HTML' },
|
|
88
|
+
plist: { language: 'XML' },
|
|
89
|
+
zsh: { language: 'Shell' },
|
|
90
|
+
fish: { language: 'Shell' },
|
|
91
|
+
command: { language: 'Shell' },
|
|
92
|
+
cfg: { language: PROPERTIES, label: 'Properties' },
|
|
93
|
+
conf: { language: PROPERTIES, label: 'Properties' },
|
|
94
|
+
ini: { language: PROPERTIES, label: 'INI' },
|
|
95
|
+
properties: { language: PROPERTIES, label: 'Properties' },
|
|
96
|
+
diff: { language: 'diff', label: 'Diff' },
|
|
97
|
+
patch: { language: 'diff', label: 'Diff' },
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** The last path segment of `path`, whichever separator it uses. */
|
|
101
|
+
export function baseName(path) {
|
|
102
|
+
const s = String(path ?? '');
|
|
103
|
+
const cut = Math.max(s.lastIndexOf('/'), s.lastIndexOf('\\'));
|
|
104
|
+
return cut === -1 ? s : s.slice(cut + 1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** The extension of a file name, lower case, without the dot. '' for a name that has none (a dotfile has none). */
|
|
108
|
+
export function extensionOf(fileName) {
|
|
109
|
+
const name = baseName(fileName);
|
|
110
|
+
const dot = name.lastIndexOf('.');
|
|
111
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : '';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* What this table says about a file name.
|
|
116
|
+
* @returns {{ language: string | null, label: string | null, known: boolean }}
|
|
117
|
+
* `known: false` → the table has no entry: ask language-data by file name.
|
|
118
|
+
* `language: null` with `known: true` → plain text, on purpose.
|
|
119
|
+
* `label: null` → show the language's own name.
|
|
120
|
+
*/
|
|
121
|
+
export function resolveLanguage(fileName) {
|
|
122
|
+
const name = baseName(fileName);
|
|
123
|
+
const lower = name.toLowerCase();
|
|
124
|
+
const hit = BY_NAME[lower]
|
|
125
|
+
|| BY_PATTERN.find((p) => p.test.test(name))
|
|
126
|
+
|| BY_EXTENSION[extensionOf(name)];
|
|
127
|
+
if (!hit) return { language: null, label: null, known: false };
|
|
128
|
+
return { language: hit.language ?? null, label: hit.label ?? null, known: true };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The word the status bar shows for a file.
|
|
133
|
+
* @param {string} fileName
|
|
134
|
+
* @param {string | null} languageName the name of the language that was found for it (language-data's), if any
|
|
135
|
+
* @returns {string | null} null → plain text: the caller shows its own translated words
|
|
136
|
+
*/
|
|
137
|
+
export function languageLabel(fileName, languageName) {
|
|
138
|
+
const { label, known, language } = resolveLanguage(fileName);
|
|
139
|
+
if (label) return label === 'Plain Text' ? null : label;
|
|
140
|
+
if (languageName) return languageName;
|
|
141
|
+
if (known && language) return language;
|
|
142
|
+
const ext = extensionOf(fileName);
|
|
143
|
+
return ext ? ext.toUpperCase() : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Whether a file is Markdown (it gets the Raw / Preview switch). */
|
|
147
|
+
export function isMarkdownFile(fileName) {
|
|
148
|
+
const ext = extensionOf(fileName);
|
|
149
|
+
return ext === 'md' || ext === 'markdown' || ext === 'mkd' || ext === 'mdx' || ext === 'mdc';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Every language name this table can ask for (tests check each one exists in the bundle). */
|
|
153
|
+
export function namedLanguages() {
|
|
154
|
+
const all = [...Object.values(BY_NAME), ...BY_PATTERN, ...Object.values(BY_EXTENSION)];
|
|
155
|
+
return [...new Set(all.map((e) => e.language).filter(Boolean))].sort();
|
|
156
|
+
}
|