feihong-code 7.5.0 → 7.6.0
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/.github/workflows/ci.yml +193 -180
- package/.github/workflows/release.yml +203 -200
- package/.github/workflows/swebench-real.yml +84 -0
- package/CHANGELOG.md +117 -94
- package/CONTRIBUTING.md +9 -2
- package/README.md +636 -609
- package/README.self-evolve.md +279 -274
- package/dist/agent/change-manager.js +1 -1
- package/dist/agent/code-rag.js +1 -1
- package/dist/agent/code-review.js +1 -1
- package/dist/agent/code-writer.js +1 -1
- package/dist/agent/completion-postprocess.js +1 -1
- package/dist/agent/context-compactor.js +11 -0
- package/dist/agent/context-compactor.js.map +1 -1
- package/dist/agent/custom-agent.js +52 -52
- package/dist/agent/design-to-code.js +40 -40
- package/dist/agent/experience.js +1 -1
- package/dist/agent/layered-memory.js +1 -1
- package/dist/agent/lint.js +1 -1
- package/dist/agent/multi-agent.js +44 -44
- package/dist/agent/orchestrator.js +93 -18
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/parallel-orchestrator.js +1 -1
- package/dist/agent/planner.js +8 -3
- package/dist/agent/planner.js.map +1 -1
- package/dist/agent/prompts.js +2 -2
- package/dist/agent/quality-gate.js +1 -1
- package/dist/agent/repo-context.js +1 -1
- package/dist/agent/repo-reader.js +1 -1
- package/dist/agent/repo-underwriter.js +1 -1
- package/dist/agent/self-correction.js +1 -1
- package/dist/agent/self-heal.js +5 -0
- package/dist/agent/self-heal.js.map +1 -1
- package/dist/agent/self-improver.js +1 -1
- package/dist/agent/subagent-summary.js +1 -1
- package/dist/agent/subagent.js +1 -1
- package/dist/agent/swe-agent.js +1 -1
- package/dist/agent/swe-verifier.js +1 -1
- package/dist/agent/symbol-index.js +1 -1
- package/dist/agent/team.js +1 -1
- package/dist/agent/type-checker.js +1 -1
- package/dist/cli/commands.js +7 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/index.js +57 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/repl.js +1 -1
- package/dist/cli/run.js +40 -3
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/self-evolve-cli.js +168 -0
- package/dist/cli/tui-run.js +1 -1
- package/dist/cli/tui.js +1 -1
- package/dist/cli/version.js +3 -3
- package/dist/cli/version.js.map +1 -1
- package/dist/enterprise/audit.js +1 -1
- package/dist/enterprise/index.js +1 -1
- package/dist/enterprise/policy.js +1 -1
- package/dist/enterprise/quota.js +1 -1
- package/dist/enterprise/tenant.js +1 -1
- package/dist/harness/executor.js +1 -1
- package/dist/harness/index.js +1 -1
- package/dist/harness/loader.js +1 -1
- package/dist/harness/types.js +1 -1
- package/dist/harness/verifier.js +52 -12
- package/dist/harness/verifier.js.map +1 -1
- package/dist/hello.js +14 -0
- package/dist/hello.js.map +1 -0
- package/dist/integrations/feishu-bridge.js +348 -0
- package/dist/integrations/feishu-bridge.js.map +1 -0
- package/dist/integrations/github-mcp.js +137 -0
- package/dist/integrations/github-mcp.js.map +1 -0
- package/dist/integrations/wechat-bridge.js +499 -0
- package/dist/integrations/wechat-bridge.js.map +1 -0
- package/dist/integrations/yuanbao-bridge.js +326 -0
- package/dist/integrations/yuanbao-bridge.js.map +1 -0
- package/dist/lsp/lsp-client.js +1 -1
- package/dist/models/model-router.js +1 -1
- package/dist/models/model.dto.js +1 -1
- package/dist/models/providers/ollama.provider.js +1 -1
- package/dist/models/providers/openai-compatible.provider.js +1 -1
- package/dist/plugins/plugin-loader.js +1 -1
- package/dist/runtime/event-log.js +1 -1
- package/dist/runtime/git.js +1 -1
- package/dist/runtime/hooks.js +71 -4
- package/dist/runtime/hooks.js.map +1 -1
- package/dist/runtime/session-persist.js +1 -1
- package/dist/runtime/worktree.js +1 -1
- package/dist/self-evolve/hook.ts +80 -0
- package/dist/self-evolve/manager.d.ts +8 -0
- package/dist/self-evolve/manager.js +517 -401
- package/dist/self-evolve/self-heal-scheduler.js +160 -0
- package/dist/self-evolve/self-heal-scheduler.js.map +1 -0
- package/dist/shared/concurrency.js +1 -1
- package/dist/shared/config.js +4 -2
- package/dist/shared/config.js.map +1 -1
- package/dist/shared/errors.js +1 -1
- package/dist/shared/i18n.js +1 -1
- package/dist/shared/i18n.js.map +1 -1
- package/dist/shared/secure-store.js +12 -5
- package/dist/shared/secure-store.js.map +1 -1
- package/dist/shared/types.js +1 -1
- package/dist/skills/goal.js +1 -1
- package/dist/skills/grill.js +1 -1
- package/dist/skills/plan.js +1 -1
- package/dist/skills/pua-hooks.js +273 -0
- package/dist/skills/pua-hooks.js.map +1 -0
- package/dist/skills/self-heal.js +9 -3
- package/dist/skills/self-heal.js.map +1 -1
- package/dist/skills/skill-loader.js +1 -1
- package/dist/skills/skill-market.js +84 -8
- package/dist/skills/skill-market.js.map +1 -1
- package/dist/tools/analysis/code-analyzer.js +1 -1
- package/dist/tools/file/edit.tool.js +18 -3
- package/dist/tools/file/edit.tool.js.map +1 -1
- package/dist/tools/file/list.tool.js +1 -1
- package/dist/tools/file/read.tool.js +1 -1
- package/dist/tools/file/write.tool.js +1 -1
- package/dist/tools/generator/code-generator.js +1 -1
- package/dist/tools/generator/test-generator.js +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/mcp/index.js +1 -1
- package/dist/tools/mcp/mcp-client.js +1 -1
- package/dist/tools/safe-path.js +1 -1
- package/dist/tools/search/grep.tool.js +1 -1
- package/dist/tools/shell/exec.js +1 -1
- package/dist/tools/shell/run-shell.tool.js +1 -1
- package/dist/tools/skills/load-skill.tool.js +1 -1
- package/dist/tools/tool.registry.js +10 -1
- package/dist/tools/tool.registry.js.map +1 -1
- package/dist/tools/verify/build-check.tool.js +1 -1
- package/dist/tools/verify/test-run.tool.js +1 -1
- package/dist/tools/web/web.tool.js +1 -1
- package/dist/voice/voice-programming.js +32 -6
- package/dist/voice/voice-programming.js.map +1 -1
- package/dist/web/auth.js +34 -2
- package/dist/web/auth.js.map +1 -1
- package/dist/web/channels.js +1 -1
- package/dist/web/public/css/style.css +1751 -1724
- package/dist/web/public/index.html +1243 -1242
- package/dist/web/public/js/api.js +558 -555
- package/dist/web/public/js/app.js +2233 -2210
- package/dist/web/public/js/mock-api.js +259 -234
- package/dist/web/public/js/monaco-editor.js +363 -363
- package/dist/web/public/js/ui.js +2217 -2251
- package/dist/web/public/js/utils.js +214 -214
- package/dist/web/public/manifest.json +59 -59
- package/dist/web/public/service-worker.js +149 -173
- package/dist/web/public/vendor/monaco/vs/abap-D-t0cyap.js +1 -0
- package/dist/web/public/vendor/monaco/vs/apex-CcIm7xu6.js +1 -0
- package/dist/web/public/vendor/monaco/vs/assets/css.worker-URu8fCFR.js +97 -0
- package/dist/web/public/vendor/monaco/vs/assets/editor.worker-lj3bdIIn.js +30 -0
- package/dist/web/public/vendor/monaco/vs/assets/editorWebWorkerMain-CA_vMoUU.js +30 -0
- package/dist/web/public/vendor/monaco/vs/assets/html.worker-D1SL3iM8.js +474 -0
- package/dist/web/public/vendor/monaco/vs/assets/json.worker-CoJx_OPf.js +62 -0
- package/dist/web/public/vendor/monaco/vs/assets/ts.worker-BWKtMYOk.js +67722 -0
- package/dist/web/public/vendor/monaco/vs/azcli-BA0tQDCg.js +1 -0
- package/dist/web/public/vendor/monaco/vs/basic-languages/monaco.contribution.js +1 -0
- package/dist/web/public/vendor/monaco/vs/bat-C397hTD6.js +1 -0
- package/dist/web/public/vendor/monaco/vs/bicep-DF5aW17k.js +2 -0
- package/dist/web/public/vendor/monaco/vs/cameligo-plsz8qhj.js +1 -0
- package/dist/web/public/vendor/monaco/vs/clojure-Y2auQMzK.js +1 -0
- package/dist/web/public/vendor/monaco/vs/coffee-Bu45yuWE.js +1 -0
- package/dist/web/public/vendor/monaco/vs/cpp-CkKPQIni.js +1 -0
- package/dist/web/public/vendor/monaco/vs/csharp-CX28MZyh.js +1 -0
- package/dist/web/public/vendor/monaco/vs/csp-D8uWnyxW.js +1 -0
- package/dist/web/public/vendor/monaco/vs/css-CaeNmE3S.js +3 -0
- package/dist/web/public/vendor/monaco/vs/css.worker-CyhWkhHo.js +1 -0
- package/dist/web/public/vendor/monaco/vs/cssMode-CV6Ay48H.js +1 -0
- package/dist/web/public/vendor/monaco/vs/cypher-DVThT8BS.js +1 -0
- package/dist/web/public/vendor/monaco/vs/dart-CmGfCvrO.js +1 -0
- package/dist/web/public/vendor/monaco/vs/dockerfile-CZqqYdch.js +1 -0
- package/dist/web/public/vendor/monaco/vs/ecl-30fUercY.js +1 -0
- package/dist/web/public/vendor/monaco/vs/editor/editor.main.css +1 -0
- package/dist/web/public/vendor/monaco/vs/editor/editor.main.js +1 -0
- package/dist/web/public/vendor/monaco/vs/editor/editor.worker.js +1 -0
- package/dist/web/public/vendor/monaco/vs/editor-KLE6jdfb.js +831 -0
- package/dist/web/public/vendor/monaco/vs/editor.js +1 -0
- package/dist/web/public/vendor/monaco/vs/editorWorkerHost-fVE1cjcC.js +35 -0
- package/dist/web/public/vendor/monaco/vs/elixir-xjPaIfzF.js +1 -0
- package/dist/web/public/vendor/monaco/vs/flow9-DqtmStfK.js +1 -0
- package/dist/web/public/vendor/monaco/vs/freemarker2-FWreY7v7.js +3 -0
- package/dist/web/public/vendor/monaco/vs/fsharp-BOMdg4U1.js +1 -0
- package/dist/web/public/vendor/monaco/vs/go-D_hbi-Jt.js +1 -0
- package/dist/web/public/vendor/monaco/vs/graphql-CKUU4kLG.js +1 -0
- package/dist/web/public/vendor/monaco/vs/handlebars-CZi4pHCH.js +1 -0
- package/dist/web/public/vendor/monaco/vs/hcl-DTaboeZW.js +1 -0
- package/dist/web/public/vendor/monaco/vs/html-j_7ZTNRU.js +1 -0
- package/dist/web/public/vendor/monaco/vs/html.worker-CA3iAimZ.js +1 -0
- package/dist/web/public/vendor/monaco/vs/htmlMode-27by_KM4.js +1 -0
- package/dist/web/public/vendor/monaco/vs/index-049xrXrp.js +1 -0
- package/dist/web/public/vendor/monaco/vs/index-CBVt3dzv.js +5 -0
- package/dist/web/public/vendor/monaco/vs/index.js +1 -0
- package/dist/web/public/vendor/monaco/vs/ini-CsNwO04R.js +1 -0
- package/dist/web/public/vendor/monaco/vs/initialize-DL0l1TGY.js +1 -0
- package/dist/web/public/vendor/monaco/vs/java-CI4ZMsH9.js +1 -0
- package/dist/web/public/vendor/monaco/vs/javascript-DTJl4Jn1.js +1 -0
- package/dist/web/public/vendor/monaco/vs/json.worker-BizpAl9O.js +1 -0
- package/dist/web/public/vendor/monaco/vs/jsonMode-DLUiMUrI.js +1 -0
- package/dist/web/public/vendor/monaco/vs/julia-BwzEvaQw.js +1 -0
- package/dist/web/public/vendor/monaco/vs/kotlin-IUYPiTV8.js +1 -0
- package/dist/web/public/vendor/monaco/vs/language/css/css.worker.js +66 -0
- package/dist/web/public/vendor/monaco/vs/language/css/monaco.contribution.js +1 -0
- package/dist/web/public/vendor/monaco/vs/language/html/html.worker.js +443 -0
- package/dist/web/public/vendor/monaco/vs/language/html/monaco.contribution.js +1 -0
- package/dist/web/public/vendor/monaco/vs/language/json/json.worker.js +17 -0
- package/dist/web/public/vendor/monaco/vs/language/json/monaco.contribution.js +1 -0
- package/dist/web/public/vendor/monaco/vs/language/typescript/monaco.contribution.js +1 -0
- package/dist/web/public/vendor/monaco/vs/language/typescript/ts.worker.js +67693 -0
- package/dist/web/public/vendor/monaco/vs/less-C0eDYdqa.js +2 -0
- package/dist/web/public/vendor/monaco/vs/lexon-iON-Kj97.js +1 -0
- package/dist/web/public/vendor/monaco/vs/liquid-CLJYelW6.js +1 -0
- package/dist/web/public/vendor/monaco/vs/loader.js +1368 -0
- package/dist/web/public/vendor/monaco/vs/lspLanguageFeatures-BIkJOWLw.js +2 -0
- package/dist/web/public/vendor/monaco/vs/lua-DtygF91M.js +1 -0
- package/dist/web/public/vendor/monaco/vs/m3-CsR4AuFi.js +1 -0
- package/dist/web/public/vendor/monaco/vs/main-BEx-Fmlo.js +15 -0
- package/dist/web/public/vendor/monaco/vs/main-DsK8pnKg.js +3 -0
- package/dist/web/public/vendor/monaco/vs/markdown-C_rD0bIw.js +1 -0
- package/dist/web/public/vendor/monaco/vs/mdx-CpzZIPHF.js +1 -0
- package/dist/web/public/vendor/monaco/vs/mips-CiYP61RB.js +1 -0
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-9cKT3C7t.js +1 -0
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-BE88ZNGY.js +1 -0
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-BPhsneLd.js +1 -0
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-BgRy6xDf.js +1 -0
- package/dist/web/public/vendor/monaco/vs/msdax-C38-sJlp.js +1 -0
- package/dist/web/public/vendor/monaco/vs/mysql-CdtbpvbG.js +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/cs.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/cs.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/de.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/de.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/es.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/es.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/fr.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/fr.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/it.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/it.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/ja.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/ja.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/ko.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/ko.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/pl.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/pl.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/pt-br.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/pt-br.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/ru.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/ru.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/tr.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/tr.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-cn.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-cn.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-tw.d.ts +1 -0
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-tw.js +9 -0
- package/dist/web/public/vendor/monaco/vs/nls.messages-loader.js +1 -0
- package/dist/web/public/vendor/monaco/vs/objective-c-CntZFaHX.js +1 -0
- package/dist/web/public/vendor/monaco/vs/pascal-r6kuqfl_.js +1 -0
- package/dist/web/public/vendor/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
- package/dist/web/public/vendor/monaco/vs/perl-DABw_TcH.js +1 -0
- package/dist/web/public/vendor/monaco/vs/pgsql-me_jFXeX.js +1 -0
- package/dist/web/public/vendor/monaco/vs/php-D_kh-9LK.js +1 -0
- package/dist/web/public/vendor/monaco/vs/pla-VfZjczW0.js +1 -0
- package/dist/web/public/vendor/monaco/vs/postiats-BBSzz8Pk.js +1 -0
- package/dist/web/public/vendor/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
- package/dist/web/public/vendor/monaco/vs/powershell-B-7ap1zc.js +1 -0
- package/dist/web/public/vendor/monaco/vs/protobuf-BmtuEB1A.js +2 -0
- package/dist/web/public/vendor/monaco/vs/pug-BRpRNeEb.js +1 -0
- package/dist/web/public/vendor/monaco/vs/python-CqWUUgfu.js +1 -0
- package/dist/web/public/vendor/monaco/vs/qsharp-BzsFaUU9.js +1 -0
- package/dist/web/public/vendor/monaco/vs/r-f8dDdrp4.js +1 -0
- package/dist/web/public/vendor/monaco/vs/razor-hok5y9Km.js +1 -0
- package/dist/web/public/vendor/monaco/vs/redis-fvZQY4PI.js +1 -0
- package/dist/web/public/vendor/monaco/vs/redshift-45Et0LQi.js +1 -0
- package/dist/web/public/vendor/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
- package/dist/web/public/vendor/monaco/vs/ruby-CZO8zYTz.js +1 -0
- package/dist/web/public/vendor/monaco/vs/rust-Bfetafyc.js +1 -0
- package/dist/web/public/vendor/monaco/vs/sb-3GYllVck.js +1 -0
- package/dist/web/public/vendor/monaco/vs/scala-foMgrKo1.js +1 -0
- package/dist/web/public/vendor/monaco/vs/scheme-CHdMtr7p.js +1 -0
- package/dist/web/public/vendor/monaco/vs/scss-C1cmLt9V.js +3 -0
- package/dist/web/public/vendor/monaco/vs/shell-ClXCKCEW.js +1 -0
- package/dist/web/public/vendor/monaco/vs/solidity-MZ6ExpPy.js +1 -0
- package/dist/web/public/vendor/monaco/vs/sophia-DWkuSsPQ.js +1 -0
- package/dist/web/public/vendor/monaco/vs/sparql-AUGFYSyk.js +1 -0
- package/dist/web/public/vendor/monaco/vs/sql-32GpJSV2.js +1 -0
- package/dist/web/public/vendor/monaco/vs/st-CuDFIVZ_.js +1 -0
- package/dist/web/public/vendor/monaco/vs/swift-t-PfMj7W.js +1 -0
- package/dist/web/public/vendor/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
- package/dist/web/public/vendor/monaco/vs/tcl-D74tq1nH.js +1 -0
- package/dist/web/public/vendor/monaco/vs/toggleHighContrast-qGX7E9o7.js +61 -0
- package/dist/web/public/vendor/monaco/vs/ts.worker-2QLmBukE.js +1 -0
- package/dist/web/public/vendor/monaco/vs/tsMode-B0S_kp2q.js +11 -0
- package/dist/web/public/vendor/monaco/vs/twig-C6taOxMV.js +1 -0
- package/dist/web/public/vendor/monaco/vs/typescript-_2t_511t.js +1 -0
- package/dist/web/public/vendor/monaco/vs/typespec-8OfoLt6R.js +1 -0
- package/dist/web/public/vendor/monaco/vs/vb-Dyb2648j.js +1 -0
- package/dist/web/public/vendor/monaco/vs/wgsl-BhLXMOR0.js +298 -0
- package/dist/web/public/vendor/monaco/vs/workers-BBttULjf.js +1 -0
- package/dist/web/public/vendor/monaco/vs/xml-cZjNDqDT.js +1 -0
- package/dist/web/public/vendor/monaco/vs/yaml-A1fOIdH6.js +1 -0
- package/dist/web/server.js +81 -16
- package/dist/web/server.js.map +1 -1
- package/dist/web/task-queue.js +6 -21
- package/dist/web/task-queue.js.map +1 -1
- package/dist/web/web-config.js +143 -0
- package/dist/web/web-config.js.map +1 -0
- package/docs/App/344/275/277/347/224/250/350/257/264/346/230/216/344/271/246.md +1 -1
- package/docs/App/346/212/200/346/234/257/350/257/264/346/230/216/344/271/246.md +1 -1
- package/docs/BENCHMARK_REPORT_en.md +386 -386
- package/docs/BENCHMARK_REPORT_zh.md +386 -386
- package/docs/DPA.md +65 -63
- package/docs/DPA/346/225/260/346/215/256/345/244/204/347/220/206/345/215/217/350/256/256/346/250/241/346/235/277_v1.0.md +69 -67
- package/docs/Deployment_Guide_EN.md +1 -1
- package/docs/SECURITY_WHITEPAPER.md +2 -2
- package/docs/SELF-EVOLVE-GUIDE.md +1 -1
- package/docs/SWE_BENCH_REPORT.md +73 -55
- package/docs/TECHNICAL_SPEC_en.md +292 -292
- package/docs/TECHNICAL_SPEC_zh.md +292 -292
- package/docs/Technical_Manual_EN.md +1 -1
- package/docs/UPDATE_NOTES_en.md +159 -159
- package/docs/UPDATE_NOTES_zh.md +159 -159
- package/docs/UPGRADE_7.2.0.md +309 -309
- package/docs/UPGRADE_GUIDE_7_5.md +86 -86
- package/docs/UPGRADE_GUIDE_7_6.md +153 -0
- package/docs/User_Manual_EN.md +1 -1
- package/docs/VOLTAGENT_SKILLS_CATALOG.md +95 -0
- package/docs/error-codes.md +1 -1
- package/docs/geo-feihong-code.html +391 -391
- package/docs//344/272/247/345/223/201/345/274/200/345/217/221/346/226/207/346/241/243.md +5 -3
- package/docs//344/275/277/347/224/250/350/257/264/346/230/216/344/271/246.md +140 -231
- package/docs//345/205/250/351/235/242/345/244/215/347/233/230_50/350/275/256/346/267/261/345/272/246/345/256/241/350/256/241_v7.2.0.md +208 -208
- package/docs//345/205/250/351/235/242/345/244/215/347/233/230_6/351/241/271/351/207/215/347/202/271/346/240/270/346/237/245_v7.2.0.md +152 -152
- package/docs//345/205/250/351/235/242/345/244/215/347/233/230_/350/277/220/350/241/214/346/227/266/345/256/236/346/265/213/347/211/210_v7.2.0.md +271 -271
- package/docs//345/220/210/350/247/204/350/256/244/350/257/201/345/207/206/345/244/207/346/270/205/345/215/225_v7.2.0.md +59 -59
- package/docs//345/256/211/345/205/250/347/231/275/347/232/256/344/271/246_v7.2.0.md +84 -82
- package/docs//346/212/200/346/234/257/350/257/264/346/230/216/344/271/246.md +240 -149
- package/docs//351/203/250/347/275/262/350/257/264/346/230/216/344/271/246.md +2 -2
- package/electron/main.js +404 -404
- package/package.json +12 -5
- package/templates/skills/api-design/SKILL.md +1 -1
- package/templates/skills/code-review/SKILL.md +1 -1
- package/templates/skills/dependency-upgrade/SKILL.md +1 -1
- package/templates/skills/doc-gen/SKILL.md +1 -1
- package/templates/skills/git-flow/SKILL.md +1 -1
- package/templates/skills/onboarding/SKILL.md +1 -1
- package/templates/skills/performance/SKILL.md +1 -1
- package/templates/skills/refactor/SKILL.md +1 -1
- package/templates/skills/security-audit/SKILL.md +1 -1
- package/templates/skills/test-gen/SKILL.md +1 -1
- package/tool-schema.json +197 -197
|
@@ -1,363 +1,363 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 飞虹 Code - Monaco Editor 封装模块
|
|
3
|
-
* VSCode 同款编辑器内核,提供语法高亮、智能补全、内联 ghost text 等能力
|
|
4
|
-
*
|
|
5
|
-
* 依赖:index.html 中已加载 vendor/monaco/vs/loader.js(AMD loader)
|
|
6
|
-
*/
|
|
7
|
-
(function (global) {
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
let monacoInstance = null;
|
|
11
|
-
let initPromise = null;
|
|
12
|
-
const editors = new Map(); // containerId -> editor instance
|
|
13
|
-
let activeEditor = null; // 最近创建的编辑器(供 lint/诊断反馈定位)
|
|
14
|
-
let inlineCompletionProvider = null;
|
|
15
|
-
let completionItemProvider = null;
|
|
16
|
-
|
|
17
|
-
/** 获取当前活跃编辑器(P6-1 lint 反馈定位) */
|
|
18
|
-
function getActiveEditor() {
|
|
19
|
-
return activeEditor;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** 根据文件扩展名推断 Monaco language id */
|
|
23
|
-
function detectLanguage(filePath) {
|
|
24
|
-
const ext = filePath.split('.').pop().toLowerCase();
|
|
25
|
-
const map = {
|
|
26
|
-
ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
|
|
27
|
-
json: 'json', md: 'markdown', html: 'html', css: 'css', scss: 'scss',
|
|
28
|
-
less: 'less', py: 'python', java: 'java', c: 'c', cpp: 'cpp', h: 'cpp',
|
|
29
|
-
cs: 'csharp', go: 'go', rs: 'rust', php: 'php', rb: 'ruby', swift: 'swift',
|
|
30
|
-
kt: 'kotlin', scala: 'scala', sh: 'shell', bash: 'shell', zsh: 'shell',
|
|
31
|
-
sql: 'sql', yaml: 'yaml', yml: 'yaml', xml: 'xml', vue: 'html', svelte: 'html',
|
|
32
|
-
proto: 'protobuf', dockerfile: 'dockerfile', toml: 'ini', ini: 'ini',
|
|
33
|
-
};
|
|
34
|
-
if (filePath.toLowerCase().endsWith('dockerfile')) return 'dockerfile';
|
|
35
|
-
return map[ext] || 'plaintext';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** 初始化 Monaco(只执行一次,返回 Promise) */
|
|
39
|
-
function initMonaco() {
|
|
40
|
-
if (monacoInstance) return Promise.resolve(monacoInstance);
|
|
41
|
-
if (initPromise) return initPromise;
|
|
42
|
-
|
|
43
|
-
initPromise = new Promise((resolve, reject) => {
|
|
44
|
-
if (typeof global.require === 'undefined') {
|
|
45
|
-
reject(new Error('Monaco loader.js 未加载,请检查 vendor/monaco/vs/loader.js'));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
global.require.config({ paths: { vs: 'vendor/monaco/vs' } });
|
|
49
|
-
global.require(['vs/editor/editor.main'], function () {
|
|
50
|
-
monacoInstance = global.monaco;
|
|
51
|
-
// 配置默认主题(跟随系统深色/浅色)
|
|
52
|
-
const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
53
|
-
monacoInstance.editor.setTheme(isDark ? 'vs-dark' : 'vs');
|
|
54
|
-
resolve(monacoInstance);
|
|
55
|
-
}, reject);
|
|
56
|
-
});
|
|
57
|
-
return initPromise;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* 在指定容器中创建 Monaco 编辑器
|
|
62
|
-
* @param {HTMLElement|string} container - 容器元素或 ID
|
|
63
|
-
* @param {string} value - 初始内容
|
|
64
|
-
* @param {string} filePath - 文件路径(用于推断语言)
|
|
65
|
-
* @param {object} options - 额外选项
|
|
66
|
-
* @returns {Promise<monaco.editor.IStandaloneCodeEditor>}
|
|
67
|
-
*/
|
|
68
|
-
async function createEditor(container, value, filePath, options = {}) {
|
|
69
|
-
const monaco = await initMonaco();
|
|
70
|
-
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
|
71
|
-
if (!el) throw new Error('编辑器容器不存在: ' + container);
|
|
72
|
-
|
|
73
|
-
// 如果该容器已有编辑器,先销毁
|
|
74
|
-
const existing = editors.get(el.id || container);
|
|
75
|
-
if (existing) {
|
|
76
|
-
existing.dispose();
|
|
77
|
-
editors.delete(el.id || container);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const language = detectLanguage(filePath);
|
|
81
|
-
const editor = monaco.editor.create(el, {
|
|
82
|
-
value: value || '',
|
|
83
|
-
language: language,
|
|
84
|
-
theme: undefined, // 使用全局主题
|
|
85
|
-
automaticLayout: true,
|
|
86
|
-
fontSize: 13,
|
|
87
|
-
lineNumbers: 'on',
|
|
88
|
-
minimap: { enabled: true },
|
|
89
|
-
scrollBeyondLastLine: false,
|
|
90
|
-
wordWrap: 'on',
|
|
91
|
-
bracketPairColorization: { enabled: true },
|
|
92
|
-
guides: { bracketPairs: true, indentation: true },
|
|
93
|
-
tabSize: 2,
|
|
94
|
-
renderWhitespace: 'selection',
|
|
95
|
-
smoothScrolling: true,
|
|
96
|
-
cursorBlinking: 'smooth',
|
|
97
|
-
// P0-1: 启用内联补全(ghost text,Tab 接受)
|
|
98
|
-
inlineSuggest: { enabled: true, showToolbar: 'always' },
|
|
99
|
-
// 启用快速建议(输入时自动触发补全弹窗)
|
|
100
|
-
quickSuggestions: { other: true, comments: false, strings: true },
|
|
101
|
-
quickSuggestionsDelay: 200,
|
|
102
|
-
...options,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const key = el.id || container;
|
|
106
|
-
editors.set(key, editor);
|
|
107
|
-
activeEditor = editor; // P6-1: 记录最近活跃编辑器供 lint 反馈定位
|
|
108
|
-
return editor;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** 获取编辑器实例 */
|
|
112
|
-
function getEditor(container) {
|
|
113
|
-
const key = typeof container === 'string' ? container : container.id;
|
|
114
|
-
return editors.get(key);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** 销毁编辑器 */
|
|
118
|
-
function disposeEditor(container) {
|
|
119
|
-
const key = typeof container === 'string' ? container : container.id;
|
|
120
|
-
const editor = editors.get(key);
|
|
121
|
-
if (editor) {
|
|
122
|
-
editor.dispose();
|
|
123
|
-
editors.delete(key);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 注册内联补全 Provider(ghost text,Tab 接受)
|
|
129
|
-
* @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset},返回 {text: string}
|
|
130
|
-
*/
|
|
131
|
-
/**
|
|
132
|
-
* 注册行内补全 Provider(ghost text)
|
|
133
|
-
* @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset, token},返回 {text} 或 null
|
|
134
|
-
* @param {{getActiveEditor?:function, onAccept?:function}} [opts] - P6-1: getActiveEditor 取当前编辑器;onAccept 在补全被接受(插入)时回调插入文本
|
|
135
|
-
*/
|
|
136
|
-
function registerInlineCompletions(fetchFn, opts) {
|
|
137
|
-
const o = opts || {};
|
|
138
|
-
initMonaco().then((monaco) => {
|
|
139
|
-
if (inlineCompletionProvider) {
|
|
140
|
-
inlineCompletionProvider.dispose();
|
|
141
|
-
}
|
|
142
|
-
let lastText = '';
|
|
143
|
-
inlineCompletionProvider = monaco.languages.registerInlineCompletionsProvider('*', {
|
|
144
|
-
provideInlineCompletions: async function (model, position, context, token) {
|
|
145
|
-
try {
|
|
146
|
-
const filePath = model.uri.path || 'untitled';
|
|
147
|
-
const content = model.getValue();
|
|
148
|
-
const offset = model.getOffsetAt(position);
|
|
149
|
-
const result = await fetchFn({ filePath, content, offset, token });
|
|
150
|
-
if (!result || !result.text) return { items: [] };
|
|
151
|
-
const item = {
|
|
152
|
-
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
|
|
153
|
-
insertText: result.text,
|
|
154
|
-
filterText: result.text.slice(0, 20),
|
|
155
|
-
};
|
|
156
|
-
lastText = result.text;
|
|
157
|
-
return { items: [item] };
|
|
158
|
-
} catch (e) {
|
|
159
|
-
return { items: [] };
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
freeInlineCompletions: function () {},
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// P6-1: accept 检测——编辑器内容变化且插入文本匹配最近补全 → onAccept(插入文本)
|
|
166
|
-
const bindAccept = function () {
|
|
167
|
-
const editor = typeof o.getActiveEditor === 'function' ? o.getActiveEditor() : null;
|
|
168
|
-
if (!editor || editor.__fhInlineAcceptBound) return;
|
|
169
|
-
editor.__fhInlineAcceptBound = true;
|
|
170
|
-
editor.onDidChangeModelContent((e) => {
|
|
171
|
-
if (!lastText) return;
|
|
172
|
-
for (const ch of e.changes || []) {
|
|
173
|
-
const ins = ch.text || '';
|
|
174
|
-
if (ins && (ins.startsWith(lastText) || lastText.startsWith(ins))) {
|
|
175
|
-
const accepted = ins.length >= lastText.length ? ins : lastText;
|
|
176
|
-
if (typeof o.onAccept === 'function') {
|
|
177
|
-
try { o.onAccept(accepted); } catch { /* lint 回调失败不阻塞编辑 */ }
|
|
178
|
-
}
|
|
179
|
-
lastText = '';
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
};
|
|
185
|
-
bindAccept();
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* 注册补全项 Provider(Ctrl+Space 触发弹窗,↑↓选择,Enter接受)
|
|
191
|
-
* @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset, prefix},返回数组 [{label, kind, insertText, detail}]
|
|
192
|
-
*/
|
|
193
|
-
function registerCompletionItems(fetchFn) {
|
|
194
|
-
initMonaco().then((monaco) => {
|
|
195
|
-
if (completionItemProvider) {
|
|
196
|
-
completionItemProvider.dispose();
|
|
197
|
-
}
|
|
198
|
-
completionItemProvider = monaco.languages.registerCompletionItemProvider('*', {
|
|
199
|
-
triggerCharacters: ['.', '(', ',', ' '],
|
|
200
|
-
provideCompletionItems: async function (model, position, context, token) {
|
|
201
|
-
try {
|
|
202
|
-
const filePath = model.uri.path || 'untitled';
|
|
203
|
-
const content = model.getValue();
|
|
204
|
-
const offset = model.getOffsetAt(position);
|
|
205
|
-
const word = model.getWordUntilPosition(position);
|
|
206
|
-
const prefix = word.word || '';
|
|
207
|
-
const suggestions = await fetchFn({ filePath, content, offset, prefix, token });
|
|
208
|
-
if (!Array.isArray(suggestions) || !suggestions.length) return { suggestions: [] };
|
|
209
|
-
const range = new monaco.Range(
|
|
210
|
-
position.lineNumber, word.startColumn,
|
|
211
|
-
position.lineNumber, position.column
|
|
212
|
-
);
|
|
213
|
-
return {
|
|
214
|
-
suggestions: suggestions.map((s) => ({
|
|
215
|
-
label: s.label || s.insertText || '',
|
|
216
|
-
kind: monaco.languages.CompletionItemKind[s.kind] || monaco.languages.CompletionItemKind.Text,
|
|
217
|
-
insertText: s.insertText || '',
|
|
218
|
-
detail: s.detail || '',
|
|
219
|
-
range: range,
|
|
220
|
-
})),
|
|
221
|
-
};
|
|
222
|
-
} catch (e) {
|
|
223
|
-
return { suggestions: [] };
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* 注册语义诊断 Provider(P4-2:编辑时错误波浪线)
|
|
232
|
-
* @param {function} fetchFn - 异步诊断函数,参数 {filePath, content},返回 [{line, column, endLine, endColumn, message, severity}]
|
|
233
|
-
* @param {function} [getEditor] - 获取当前编辑器实例(默认遍历已注册编辑器)
|
|
234
|
-
*/
|
|
235
|
-
function registerDiagnostics(fetchFn, getActiveEditor) {
|
|
236
|
-
initMonaco().then((monaco) => {
|
|
237
|
-
// P5-2: 诊断 hover 提示——hover 到有 marker 的行时展示错误/警告详情
|
|
238
|
-
const langSet = [
|
|
239
|
-
'typescript', 'javascript', 'json', 'markdown', 'html', 'css', 'scss', 'less',
|
|
240
|
-
'python', 'java', 'c', 'cpp', 'csharp', 'go', 'rust', 'php', 'ruby', 'swift',
|
|
241
|
-
'kotlin', 'scala', 'shell', 'sql', 'yaml', 'xml', 'protobuf', 'dockerfile',
|
|
242
|
-
'ini', 'plaintext',
|
|
243
|
-
];
|
|
244
|
-
const hoverDisp = monaco.languages.registerHoverProvider(langSet, {
|
|
245
|
-
provideHover(model, position) {
|
|
246
|
-
let editor;
|
|
247
|
-
try { editor = typeof getActiveEditor === 'function' ? getActiveEditor() : null; } catch { editor = null; }
|
|
248
|
-
if (!editor || editor.isDisposed?.()) return null;
|
|
249
|
-
const cur = editor.getModel();
|
|
250
|
-
if (!cur || model.uri.toString() !== cur.uri.toString()) return null;
|
|
251
|
-
const markers = monaco.editor.getModelMarkers({ resource: model.uri })
|
|
252
|
-
.filter((x) => x.startLineNumber <= position.lineNumber && position.lineNumber <= x.endLineNumber);
|
|
253
|
-
if (markers.length === 0) return null;
|
|
254
|
-
const md = markers.map((x) => {
|
|
255
|
-
const sevName = x.severity === monaco.MarkerSeverity.Error ? '错误' : x.severity === monaco.MarkerSeverity.Warning ? '警告' : '提示';
|
|
256
|
-
const icon = x.severity === monaco.MarkerSeverity.Error ? '⛔' : x.severity === monaco.MarkerSeverity.Warning ? '⚠️' : 'ℹ️';
|
|
257
|
-
return '**' + icon + ' ' + sevName + '** (L' + x.startLineNumber + ':' + x.startColumn + ') \n' + x.message;
|
|
258
|
-
}).join('\n\n---\n\n');
|
|
259
|
-
return {
|
|
260
|
-
contents: [{ value: '#### 飞虹 Code 诊断\n\n' + md, isTrusted: true }],
|
|
261
|
-
range: new monaco.Range(position.lineNumber, 1, position.lineNumber, 1),
|
|
262
|
-
};
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
hoverDisp; // 引用保持存活(Monaco 内部维护)
|
|
266
|
-
|
|
267
|
-
// 防抖 + 单飞,避免编辑时高频请求
|
|
268
|
-
let timer = null;
|
|
269
|
-
let inflight = false;
|
|
270
|
-
let queued = false;
|
|
271
|
-
const run = async function () {
|
|
272
|
-
if (inflight) { queued = true; return; }
|
|
273
|
-
const editor = typeof getActiveEditor === 'function' ? getActiveEditor() : null;
|
|
274
|
-
if (!editor || editor.isDisposed?.()) return;
|
|
275
|
-
const model = editor.getModel();
|
|
276
|
-
if (!model) return;
|
|
277
|
-
inflight = true;
|
|
278
|
-
try {
|
|
279
|
-
const filePath = model.uri.path || 'untitled';
|
|
280
|
-
const content = model.getValue();
|
|
281
|
-
const result = await fetchFn({ filePath, content });
|
|
282
|
-
if (!result || !Array.isArray(result.diagnostics)) {
|
|
283
|
-
monaco.editor.setModelMarkers(model, 'fhcode', []);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
const markers = result.diagnostics.map((d) => {
|
|
287
|
-
const sev = d.severity === 1 || d.severity === 'error'
|
|
288
|
-
? monaco.MarkerSeverity.Error
|
|
289
|
-
: d.severity === 2 || d.severity === 'warning'
|
|
290
|
-
? monaco.MarkerSeverity.Warning
|
|
291
|
-
: monaco.MarkerSeverity.Info;
|
|
292
|
-
return {
|
|
293
|
-
severity: sev,
|
|
294
|
-
message: d.message || '',
|
|
295
|
-
startLineNumber: d.line ?? 1,
|
|
296
|
-
startColumn: d.column ?? 1,
|
|
297
|
-
endLineNumber: d.endLine ?? (d.line ?? 1),
|
|
298
|
-
endColumn: d.endColumn ?? (d.column ?? 1) + (d.message?.length || 8),
|
|
299
|
-
};
|
|
300
|
-
});
|
|
301
|
-
monaco.editor.setModelMarkers(model, 'fhcode', markers);
|
|
302
|
-
} catch {
|
|
303
|
-
/* 诊断失败静默(不阻塞编辑) */
|
|
304
|
-
} finally {
|
|
305
|
-
inflight = false;
|
|
306
|
-
if (queued) { queued = false; timer = setTimeout(run, 200); }
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
const schedule = function () {
|
|
310
|
-
clearTimeout(timer);
|
|
311
|
-
timer = setTimeout(run, 350);
|
|
312
|
-
};
|
|
313
|
-
// 绑定现有编辑器(createEditor 后由 ui 层调用 attachDiagnostics 触发监听)
|
|
314
|
-
const editor = typeof getActiveEditor === 'function' ? getActiveEditor() : null;
|
|
315
|
-
if (editor && !editor.__fhDiagnosticsBound) {
|
|
316
|
-
editor.__fhDiagnosticsBound = true;
|
|
317
|
-
editor.onDidChangeModelContent(schedule);
|
|
318
|
-
editor.onDidChangeModel(() => schedule());
|
|
319
|
-
}
|
|
320
|
-
return { run, schedule };
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* P6-1: 补全接受后的 lint 反馈——把 /api/lint 结果追加为编辑器波浪线(owner: fhcode-lint)
|
|
326
|
-
* @param {Array} issues - [{severity, line, column, message}]
|
|
327
|
-
*/
|
|
328
|
-
function showLintFeedback(issues) {
|
|
329
|
-
initMonaco().then((monaco) => {
|
|
330
|
-
const editor = activeEditor;
|
|
331
|
-
if (!editor || editor.isDisposed?.()) return;
|
|
332
|
-
const model = editor.getModel();
|
|
333
|
-
if (!model) return;
|
|
334
|
-
if (!Array.isArray(issues) || issues.length === 0) {
|
|
335
|
-
monaco.editor.setModelMarkers(model, 'fhcode-lint', []);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
const markers = issues.map((d) => ({
|
|
339
|
-
severity: d.severity === 1 ? monaco.MarkerSeverity.Error : d.severity === 2 ? monaco.MarkerSeverity.Warning : monaco.MarkerSeverity.Info,
|
|
340
|
-
message: '补全校验:' + (d.message || ''),
|
|
341
|
-
startLineNumber: d.line ?? 1,
|
|
342
|
-
startColumn: d.column ?? 1,
|
|
343
|
-
endLineNumber: d.line ?? 1,
|
|
344
|
-
endColumn: (d.column ?? 1) + (d.message?.length || 20),
|
|
345
|
-
}));
|
|
346
|
-
monaco.editor.setModelMarkers(model, 'fhcode-lint', markers);
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/** 暴露全局 API */
|
|
351
|
-
global.FHMonaco = {
|
|
352
|
-
init: initMonaco,
|
|
353
|
-
createEditor: createEditor,
|
|
354
|
-
getEditor: getEditor,
|
|
355
|
-
disposeEditor: disposeEditor,
|
|
356
|
-
detectLanguage: detectLanguage,
|
|
357
|
-
getActiveEditor: getActiveEditor,
|
|
358
|
-
registerInlineCompletions: registerInlineCompletions,
|
|
359
|
-
registerCompletionItems: registerCompletionItems,
|
|
360
|
-
registerDiagnostics: registerDiagnostics,
|
|
361
|
-
showLintFeedback: showLintFeedback,
|
|
362
|
-
};
|
|
363
|
-
})(window);
|
|
1
|
+
/**
|
|
2
|
+
* 飞虹 Code - Monaco Editor 封装模块
|
|
3
|
+
* VSCode 同款编辑器内核,提供语法高亮、智能补全、内联 ghost text 等能力
|
|
4
|
+
*
|
|
5
|
+
* 依赖:index.html 中已加载 vendor/monaco/vs/loader.js(AMD loader)
|
|
6
|
+
*/
|
|
7
|
+
(function (global) {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
let monacoInstance = null;
|
|
11
|
+
let initPromise = null;
|
|
12
|
+
const editors = new Map(); // containerId -> editor instance
|
|
13
|
+
let activeEditor = null; // 最近创建的编辑器(供 lint/诊断反馈定位)
|
|
14
|
+
let inlineCompletionProvider = null;
|
|
15
|
+
let completionItemProvider = null;
|
|
16
|
+
|
|
17
|
+
/** 获取当前活跃编辑器(P6-1 lint 反馈定位) */
|
|
18
|
+
function getActiveEditor() {
|
|
19
|
+
return activeEditor;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 根据文件扩展名推断 Monaco language id */
|
|
23
|
+
function detectLanguage(filePath) {
|
|
24
|
+
const ext = filePath.split('.').pop().toLowerCase();
|
|
25
|
+
const map = {
|
|
26
|
+
ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
|
|
27
|
+
json: 'json', md: 'markdown', html: 'html', css: 'css', scss: 'scss',
|
|
28
|
+
less: 'less', py: 'python', java: 'java', c: 'c', cpp: 'cpp', h: 'cpp',
|
|
29
|
+
cs: 'csharp', go: 'go', rs: 'rust', php: 'php', rb: 'ruby', swift: 'swift',
|
|
30
|
+
kt: 'kotlin', scala: 'scala', sh: 'shell', bash: 'shell', zsh: 'shell',
|
|
31
|
+
sql: 'sql', yaml: 'yaml', yml: 'yaml', xml: 'xml', vue: 'html', svelte: 'html',
|
|
32
|
+
proto: 'protobuf', dockerfile: 'dockerfile', toml: 'ini', ini: 'ini',
|
|
33
|
+
};
|
|
34
|
+
if (filePath.toLowerCase().endsWith('dockerfile')) return 'dockerfile';
|
|
35
|
+
return map[ext] || 'plaintext';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 初始化 Monaco(只执行一次,返回 Promise) */
|
|
39
|
+
function initMonaco() {
|
|
40
|
+
if (monacoInstance) return Promise.resolve(monacoInstance);
|
|
41
|
+
if (initPromise) return initPromise;
|
|
42
|
+
|
|
43
|
+
initPromise = new Promise((resolve, reject) => {
|
|
44
|
+
if (typeof global.require === 'undefined') {
|
|
45
|
+
reject(new Error('Monaco loader.js 未加载,请检查 vendor/monaco/vs/loader.js'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
global.require.config({ paths: { vs: 'vendor/monaco/vs' } });
|
|
49
|
+
global.require(['vs/editor/editor.main'], function () {
|
|
50
|
+
monacoInstance = global.monaco;
|
|
51
|
+
// 配置默认主题(跟随系统深色/浅色)
|
|
52
|
+
const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
53
|
+
monacoInstance.editor.setTheme(isDark ? 'vs-dark' : 'vs');
|
|
54
|
+
resolve(monacoInstance);
|
|
55
|
+
}, reject);
|
|
56
|
+
});
|
|
57
|
+
return initPromise;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 在指定容器中创建 Monaco 编辑器
|
|
62
|
+
* @param {HTMLElement|string} container - 容器元素或 ID
|
|
63
|
+
* @param {string} value - 初始内容
|
|
64
|
+
* @param {string} filePath - 文件路径(用于推断语言)
|
|
65
|
+
* @param {object} options - 额外选项
|
|
66
|
+
* @returns {Promise<monaco.editor.IStandaloneCodeEditor>}
|
|
67
|
+
*/
|
|
68
|
+
async function createEditor(container, value, filePath, options = {}) {
|
|
69
|
+
const monaco = await initMonaco();
|
|
70
|
+
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
|
71
|
+
if (!el) throw new Error('编辑器容器不存在: ' + container);
|
|
72
|
+
|
|
73
|
+
// 如果该容器已有编辑器,先销毁
|
|
74
|
+
const existing = editors.get(el.id || container);
|
|
75
|
+
if (existing) {
|
|
76
|
+
existing.dispose();
|
|
77
|
+
editors.delete(el.id || container);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const language = detectLanguage(filePath);
|
|
81
|
+
const editor = monaco.editor.create(el, {
|
|
82
|
+
value: value || '',
|
|
83
|
+
language: language,
|
|
84
|
+
theme: undefined, // 使用全局主题
|
|
85
|
+
automaticLayout: true,
|
|
86
|
+
fontSize: 13,
|
|
87
|
+
lineNumbers: 'on',
|
|
88
|
+
minimap: { enabled: true },
|
|
89
|
+
scrollBeyondLastLine: false,
|
|
90
|
+
wordWrap: 'on',
|
|
91
|
+
bracketPairColorization: { enabled: true },
|
|
92
|
+
guides: { bracketPairs: true, indentation: true },
|
|
93
|
+
tabSize: 2,
|
|
94
|
+
renderWhitespace: 'selection',
|
|
95
|
+
smoothScrolling: true,
|
|
96
|
+
cursorBlinking: 'smooth',
|
|
97
|
+
// P0-1: 启用内联补全(ghost text,Tab 接受)
|
|
98
|
+
inlineSuggest: { enabled: true, showToolbar: 'always' },
|
|
99
|
+
// 启用快速建议(输入时自动触发补全弹窗)
|
|
100
|
+
quickSuggestions: { other: true, comments: false, strings: true },
|
|
101
|
+
quickSuggestionsDelay: 200,
|
|
102
|
+
...options,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const key = el.id || container;
|
|
106
|
+
editors.set(key, editor);
|
|
107
|
+
activeEditor = editor; // P6-1: 记录最近活跃编辑器供 lint 反馈定位
|
|
108
|
+
return editor;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** 获取编辑器实例 */
|
|
112
|
+
function getEditor(container) {
|
|
113
|
+
const key = typeof container === 'string' ? container : container.id;
|
|
114
|
+
return editors.get(key);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 销毁编辑器 */
|
|
118
|
+
function disposeEditor(container) {
|
|
119
|
+
const key = typeof container === 'string' ? container : container.id;
|
|
120
|
+
const editor = editors.get(key);
|
|
121
|
+
if (editor) {
|
|
122
|
+
editor.dispose();
|
|
123
|
+
editors.delete(key);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 注册内联补全 Provider(ghost text,Tab 接受)
|
|
129
|
+
* @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset},返回 {text: string}
|
|
130
|
+
*/
|
|
131
|
+
/**
|
|
132
|
+
* 注册行内补全 Provider(ghost text)
|
|
133
|
+
* @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset, token},返回 {text} 或 null
|
|
134
|
+
* @param {{getActiveEditor?:function, onAccept?:function}} [opts] - P6-1: getActiveEditor 取当前编辑器;onAccept 在补全被接受(插入)时回调插入文本
|
|
135
|
+
*/
|
|
136
|
+
function registerInlineCompletions(fetchFn, opts) {
|
|
137
|
+
const o = opts || {};
|
|
138
|
+
initMonaco().then((monaco) => {
|
|
139
|
+
if (inlineCompletionProvider) {
|
|
140
|
+
inlineCompletionProvider.dispose();
|
|
141
|
+
}
|
|
142
|
+
let lastText = '';
|
|
143
|
+
inlineCompletionProvider = monaco.languages.registerInlineCompletionsProvider('*', {
|
|
144
|
+
provideInlineCompletions: async function (model, position, context, token) {
|
|
145
|
+
try {
|
|
146
|
+
const filePath = model.uri.path || 'untitled';
|
|
147
|
+
const content = model.getValue();
|
|
148
|
+
const offset = model.getOffsetAt(position);
|
|
149
|
+
const result = await fetchFn({ filePath, content, offset, token });
|
|
150
|
+
if (!result || !result.text) return { items: [] };
|
|
151
|
+
const item = {
|
|
152
|
+
range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
|
|
153
|
+
insertText: result.text,
|
|
154
|
+
filterText: result.text.slice(0, 20),
|
|
155
|
+
};
|
|
156
|
+
lastText = result.text;
|
|
157
|
+
return { items: [item] };
|
|
158
|
+
} catch (e) {
|
|
159
|
+
return { items: [] };
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
freeInlineCompletions: function () {},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// P6-1: accept 检测——编辑器内容变化且插入文本匹配最近补全 → onAccept(插入文本)
|
|
166
|
+
const bindAccept = function () {
|
|
167
|
+
const editor = typeof o.getActiveEditor === 'function' ? o.getActiveEditor() : null;
|
|
168
|
+
if (!editor || editor.__fhInlineAcceptBound) return;
|
|
169
|
+
editor.__fhInlineAcceptBound = true;
|
|
170
|
+
editor.onDidChangeModelContent((e) => {
|
|
171
|
+
if (!lastText) return;
|
|
172
|
+
for (const ch of e.changes || []) {
|
|
173
|
+
const ins = ch.text || '';
|
|
174
|
+
if (ins && (ins.startsWith(lastText) || lastText.startsWith(ins))) {
|
|
175
|
+
const accepted = ins.length >= lastText.length ? ins : lastText;
|
|
176
|
+
if (typeof o.onAccept === 'function') {
|
|
177
|
+
try { o.onAccept(accepted); } catch { /* lint 回调失败不阻塞编辑 */ }
|
|
178
|
+
}
|
|
179
|
+
lastText = '';
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
bindAccept();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 注册补全项 Provider(Ctrl+Space 触发弹窗,↑↓选择,Enter接受)
|
|
191
|
+
* @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset, prefix},返回数组 [{label, kind, insertText, detail}]
|
|
192
|
+
*/
|
|
193
|
+
function registerCompletionItems(fetchFn) {
|
|
194
|
+
initMonaco().then((monaco) => {
|
|
195
|
+
if (completionItemProvider) {
|
|
196
|
+
completionItemProvider.dispose();
|
|
197
|
+
}
|
|
198
|
+
completionItemProvider = monaco.languages.registerCompletionItemProvider('*', {
|
|
199
|
+
triggerCharacters: ['.', '(', ',', ' '],
|
|
200
|
+
provideCompletionItems: async function (model, position, context, token) {
|
|
201
|
+
try {
|
|
202
|
+
const filePath = model.uri.path || 'untitled';
|
|
203
|
+
const content = model.getValue();
|
|
204
|
+
const offset = model.getOffsetAt(position);
|
|
205
|
+
const word = model.getWordUntilPosition(position);
|
|
206
|
+
const prefix = word.word || '';
|
|
207
|
+
const suggestions = await fetchFn({ filePath, content, offset, prefix, token });
|
|
208
|
+
if (!Array.isArray(suggestions) || !suggestions.length) return { suggestions: [] };
|
|
209
|
+
const range = new monaco.Range(
|
|
210
|
+
position.lineNumber, word.startColumn,
|
|
211
|
+
position.lineNumber, position.column
|
|
212
|
+
);
|
|
213
|
+
return {
|
|
214
|
+
suggestions: suggestions.map((s) => ({
|
|
215
|
+
label: s.label || s.insertText || '',
|
|
216
|
+
kind: monaco.languages.CompletionItemKind[s.kind] || monaco.languages.CompletionItemKind.Text,
|
|
217
|
+
insertText: s.insertText || '',
|
|
218
|
+
detail: s.detail || '',
|
|
219
|
+
range: range,
|
|
220
|
+
})),
|
|
221
|
+
};
|
|
222
|
+
} catch (e) {
|
|
223
|
+
return { suggestions: [] };
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 注册语义诊断 Provider(P4-2:编辑时错误波浪线)
|
|
232
|
+
* @param {function} fetchFn - 异步诊断函数,参数 {filePath, content},返回 [{line, column, endLine, endColumn, message, severity}]
|
|
233
|
+
* @param {function} [getEditor] - 获取当前编辑器实例(默认遍历已注册编辑器)
|
|
234
|
+
*/
|
|
235
|
+
function registerDiagnostics(fetchFn, getActiveEditor) {
|
|
236
|
+
initMonaco().then((monaco) => {
|
|
237
|
+
// P5-2: 诊断 hover 提示——hover 到有 marker 的行时展示错误/警告详情
|
|
238
|
+
const langSet = [
|
|
239
|
+
'typescript', 'javascript', 'json', 'markdown', 'html', 'css', 'scss', 'less',
|
|
240
|
+
'python', 'java', 'c', 'cpp', 'csharp', 'go', 'rust', 'php', 'ruby', 'swift',
|
|
241
|
+
'kotlin', 'scala', 'shell', 'sql', 'yaml', 'xml', 'protobuf', 'dockerfile',
|
|
242
|
+
'ini', 'plaintext',
|
|
243
|
+
];
|
|
244
|
+
const hoverDisp = monaco.languages.registerHoverProvider(langSet, {
|
|
245
|
+
provideHover(model, position) {
|
|
246
|
+
let editor;
|
|
247
|
+
try { editor = typeof getActiveEditor === 'function' ? getActiveEditor() : null; } catch { editor = null; }
|
|
248
|
+
if (!editor || editor.isDisposed?.()) return null;
|
|
249
|
+
const cur = editor.getModel();
|
|
250
|
+
if (!cur || model.uri.toString() !== cur.uri.toString()) return null;
|
|
251
|
+
const markers = monaco.editor.getModelMarkers({ resource: model.uri })
|
|
252
|
+
.filter((x) => x.startLineNumber <= position.lineNumber && position.lineNumber <= x.endLineNumber);
|
|
253
|
+
if (markers.length === 0) return null;
|
|
254
|
+
const md = markers.map((x) => {
|
|
255
|
+
const sevName = x.severity === monaco.MarkerSeverity.Error ? '错误' : x.severity === monaco.MarkerSeverity.Warning ? '警告' : '提示';
|
|
256
|
+
const icon = x.severity === monaco.MarkerSeverity.Error ? '⛔' : x.severity === monaco.MarkerSeverity.Warning ? '⚠️' : 'ℹ️';
|
|
257
|
+
return '**' + icon + ' ' + sevName + '** (L' + x.startLineNumber + ':' + x.startColumn + ') \n' + x.message;
|
|
258
|
+
}).join('\n\n---\n\n');
|
|
259
|
+
return {
|
|
260
|
+
contents: [{ value: '#### 飞虹 Code 诊断\n\n' + md, isTrusted: true }],
|
|
261
|
+
range: new monaco.Range(position.lineNumber, 1, position.lineNumber, 1),
|
|
262
|
+
};
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
hoverDisp; // 引用保持存活(Monaco 内部维护)
|
|
266
|
+
|
|
267
|
+
// 防抖 + 单飞,避免编辑时高频请求
|
|
268
|
+
let timer = null;
|
|
269
|
+
let inflight = false;
|
|
270
|
+
let queued = false;
|
|
271
|
+
const run = async function () {
|
|
272
|
+
if (inflight) { queued = true; return; }
|
|
273
|
+
const editor = typeof getActiveEditor === 'function' ? getActiveEditor() : null;
|
|
274
|
+
if (!editor || editor.isDisposed?.()) return;
|
|
275
|
+
const model = editor.getModel();
|
|
276
|
+
if (!model) return;
|
|
277
|
+
inflight = true;
|
|
278
|
+
try {
|
|
279
|
+
const filePath = model.uri.path || 'untitled';
|
|
280
|
+
const content = model.getValue();
|
|
281
|
+
const result = await fetchFn({ filePath, content });
|
|
282
|
+
if (!result || !Array.isArray(result.diagnostics)) {
|
|
283
|
+
monaco.editor.setModelMarkers(model, 'fhcode', []);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const markers = result.diagnostics.map((d) => {
|
|
287
|
+
const sev = d.severity === 1 || d.severity === 'error'
|
|
288
|
+
? monaco.MarkerSeverity.Error
|
|
289
|
+
: d.severity === 2 || d.severity === 'warning'
|
|
290
|
+
? monaco.MarkerSeverity.Warning
|
|
291
|
+
: monaco.MarkerSeverity.Info;
|
|
292
|
+
return {
|
|
293
|
+
severity: sev,
|
|
294
|
+
message: d.message || '',
|
|
295
|
+
startLineNumber: d.line ?? 1,
|
|
296
|
+
startColumn: d.column ?? 1,
|
|
297
|
+
endLineNumber: d.endLine ?? (d.line ?? 1),
|
|
298
|
+
endColumn: d.endColumn ?? (d.column ?? 1) + (d.message?.length || 8),
|
|
299
|
+
};
|
|
300
|
+
});
|
|
301
|
+
monaco.editor.setModelMarkers(model, 'fhcode', markers);
|
|
302
|
+
} catch {
|
|
303
|
+
/* 诊断失败静默(不阻塞编辑) */
|
|
304
|
+
} finally {
|
|
305
|
+
inflight = false;
|
|
306
|
+
if (queued) { queued = false; timer = setTimeout(run, 200); }
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const schedule = function () {
|
|
310
|
+
clearTimeout(timer);
|
|
311
|
+
timer = setTimeout(run, 350);
|
|
312
|
+
};
|
|
313
|
+
// 绑定现有编辑器(createEditor 后由 ui 层调用 attachDiagnostics 触发监听)
|
|
314
|
+
const editor = typeof getActiveEditor === 'function' ? getActiveEditor() : null;
|
|
315
|
+
if (editor && !editor.__fhDiagnosticsBound) {
|
|
316
|
+
editor.__fhDiagnosticsBound = true;
|
|
317
|
+
editor.onDidChangeModelContent(schedule);
|
|
318
|
+
editor.onDidChangeModel(() => schedule());
|
|
319
|
+
}
|
|
320
|
+
return { run, schedule };
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* P6-1: 补全接受后的 lint 反馈——把 /api/lint 结果追加为编辑器波浪线(owner: fhcode-lint)
|
|
326
|
+
* @param {Array} issues - [{severity, line, column, message}]
|
|
327
|
+
*/
|
|
328
|
+
function showLintFeedback(issues) {
|
|
329
|
+
initMonaco().then((monaco) => {
|
|
330
|
+
const editor = activeEditor;
|
|
331
|
+
if (!editor || editor.isDisposed?.()) return;
|
|
332
|
+
const model = editor.getModel();
|
|
333
|
+
if (!model) return;
|
|
334
|
+
if (!Array.isArray(issues) || issues.length === 0) {
|
|
335
|
+
monaco.editor.setModelMarkers(model, 'fhcode-lint', []);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const markers = issues.map((d) => ({
|
|
339
|
+
severity: d.severity === 1 ? monaco.MarkerSeverity.Error : d.severity === 2 ? monaco.MarkerSeverity.Warning : monaco.MarkerSeverity.Info,
|
|
340
|
+
message: '补全校验:' + (d.message || ''),
|
|
341
|
+
startLineNumber: d.line ?? 1,
|
|
342
|
+
startColumn: d.column ?? 1,
|
|
343
|
+
endLineNumber: d.line ?? 1,
|
|
344
|
+
endColumn: (d.column ?? 1) + (d.message?.length || 20),
|
|
345
|
+
}));
|
|
346
|
+
monaco.editor.setModelMarkers(model, 'fhcode-lint', markers);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** 暴露全局 API */
|
|
351
|
+
global.FHMonaco = {
|
|
352
|
+
init: initMonaco,
|
|
353
|
+
createEditor: createEditor,
|
|
354
|
+
getEditor: getEditor,
|
|
355
|
+
disposeEditor: disposeEditor,
|
|
356
|
+
detectLanguage: detectLanguage,
|
|
357
|
+
getActiveEditor: getActiveEditor,
|
|
358
|
+
registerInlineCompletions: registerInlineCompletions,
|
|
359
|
+
registerCompletionItems: registerCompletionItems,
|
|
360
|
+
registerDiagnostics: registerDiagnostics,
|
|
361
|
+
showLintFeedback: showLintFeedback,
|
|
362
|
+
};
|
|
363
|
+
})(window);
|