feihong-code 7.2.0 → 7.5.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 +180 -140
- package/.github/workflows/release.yml +200 -173
- package/CHANGELOG.md +94 -115
- package/README.md +609 -586
- package/dist/agent/completion-engine.js +76 -45
- package/dist/agent/completion-engine.js.map +1 -1
- package/dist/agent/completion-postprocess.js +109 -0
- package/dist/agent/completion-postprocess.js.map +1 -0
- package/dist/agent/context-budget.js +121 -0
- package/dist/agent/context-budget.js.map +1 -0
- package/dist/agent/context-compactor.js +47 -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/lint.js +121 -0
- package/dist/agent/lint.js.map +1 -0
- package/dist/agent/multi-agent.js +44 -44
- package/dist/agent/orchestrator.js +20 -20
- package/dist/agent/prompts.js +54 -54
- package/dist/agent/self-correction.js +24 -24
- package/dist/agent/self-heal.js +27 -27
- package/dist/cli/commands.js +9 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/multi-pane-tui.js +217 -0
- package/dist/cli/multi-pane-tui.js.map +1 -0
- package/dist/cli/run.js +129 -40
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/tui-run.js +79 -0
- package/dist/cli/tui-run.js.map +1 -0
- package/dist/cli/tui.js.map +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/lsp/lsp-client.js +183 -0
- package/dist/lsp/lsp-client.js.map +1 -0
- package/dist/lsp/lsp-service.js +159 -0
- package/dist/lsp/lsp-service.js.map +1 -0
- package/dist/plugins/manager.js +50 -2
- package/dist/plugins/manager.js.map +1 -1
- package/dist/shared/i18n.js +108 -100
- package/dist/shared/i18n.js.map +1 -1
- package/dist/tools/generator/code-generator.js +40 -40
- package/dist/tools/generator/test-generator.js +9 -9
- package/dist/tools/shell/exec.js +23 -1
- package/dist/tools/shell/exec.js.map +1 -1
- package/dist/web/extra-apis.js +440 -0
- package/dist/web/extra-apis.js.map +1 -0
- package/dist/web/public/css/style.css +1715 -1715
- package/dist/web/public/index.html +1242 -1200
- package/dist/web/public/js/api.js +555 -496
- package/dist/web/public/js/app.js +2210 -2210
- package/dist/web/public/js/mock-api.js +234 -234
- package/dist/web/public/js/monaco-editor.js +363 -203
- package/dist/web/public/js/ui.js +2251 -1981
- package/dist/web/public/js/utils.js +214 -214
- package/dist/web/public/manifest.json +59 -59
- package/dist/web/public/service-worker.js +173 -173
- package/dist/web/server.js +97 -45
- package/dist/web/server.js.map +1 -1
- package/docs/BENCHMARK_REPORT_en.md +386 -329
- package/docs/BENCHMARK_REPORT_zh.md +386 -329
- package/docs/DPA.md +63 -0
- 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 +67 -0
- package/docs/SECURITY_WHITEPAPER.md +74 -0
- package/docs/SWE_BENCH_REPORT.md +55 -0
- package/docs/TECHNICAL_SPEC_en.md +292 -292
- package/docs/TECHNICAL_SPEC_zh.md +292 -292
- package/docs/UPDATE_NOTES_en.md +159 -159
- package/docs/UPDATE_NOTES_zh.md +159 -159
- package/docs/UPGRADE_7.2.0.md +309 -0
- package/docs/UPGRADE_GUIDE_7_5.md +86 -0
- package/docs/geo-feihong-code.html +391 -0
- 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 -0
- 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 -0
- 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 -0
- 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 -0
- package/docs//345/256/211/345/205/250/347/231/275/347/232/256/344/271/246_v7.2.0.md +82 -0
- package/electron/main.js +404 -602
- package/package.json +175 -172
- package/templates/skills/api-design/SKILL.md +27 -0
- package/templates/skills/code-review/SKILL.md +35 -0
- package/templates/skills/dependency-upgrade/SKILL.md +29 -0
- package/templates/skills/doc-gen/SKILL.md +29 -0
- package/templates/skills/git-flow/SKILL.md +26 -0
- package/templates/skills/onboarding/SKILL.md +27 -0
- package/templates/skills/performance/SKILL.md +29 -0
- package/templates/skills/refactor/SKILL.md +28 -0
- package/templates/skills/security-audit/SKILL.md +28 -0
- package/templates/skills/test-gen/SKILL.md +30 -0
- package/tool-schema.json +197 -197
- package/dist/hello.js +0 -14
- package/dist/hello.js.map +0 -1
- package/dist/self-evolve/hook.ts +0 -80
- package/dist/self-evolve/manager.d.ts +0 -8
- package/dist/web/public/vendor/monaco/vs/abap-D-t0cyap.js +0 -1
- package/dist/web/public/vendor/monaco/vs/apex-CcIm7xu6.js +0 -1
- package/dist/web/public/vendor/monaco/vs/assets/css.worker-URu8fCFR.js +0 -97
- package/dist/web/public/vendor/monaco/vs/assets/editor.worker-lj3bdIIn.js +0 -30
- package/dist/web/public/vendor/monaco/vs/assets/editorWebWorkerMain-CA_vMoUU.js +0 -30
- package/dist/web/public/vendor/monaco/vs/assets/html.worker-D1SL3iM8.js +0 -474
- package/dist/web/public/vendor/monaco/vs/assets/json.worker-CoJx_OPf.js +0 -62
- package/dist/web/public/vendor/monaco/vs/assets/ts.worker-BWKtMYOk.js +0 -67722
- package/dist/web/public/vendor/monaco/vs/azcli-BA0tQDCg.js +0 -1
- package/dist/web/public/vendor/monaco/vs/basic-languages/monaco.contribution.js +0 -1
- package/dist/web/public/vendor/monaco/vs/bat-C397hTD6.js +0 -1
- package/dist/web/public/vendor/monaco/vs/bicep-DF5aW17k.js +0 -2
- package/dist/web/public/vendor/monaco/vs/cameligo-plsz8qhj.js +0 -1
- package/dist/web/public/vendor/monaco/vs/clojure-Y2auQMzK.js +0 -1
- package/dist/web/public/vendor/monaco/vs/coffee-Bu45yuWE.js +0 -1
- package/dist/web/public/vendor/monaco/vs/cpp-CkKPQIni.js +0 -1
- package/dist/web/public/vendor/monaco/vs/csharp-CX28MZyh.js +0 -1
- package/dist/web/public/vendor/monaco/vs/csp-D8uWnyxW.js +0 -1
- package/dist/web/public/vendor/monaco/vs/css-CaeNmE3S.js +0 -3
- package/dist/web/public/vendor/monaco/vs/css.worker-CyhWkhHo.js +0 -1
- package/dist/web/public/vendor/monaco/vs/cssMode-CV6Ay48H.js +0 -1
- package/dist/web/public/vendor/monaco/vs/cypher-DVThT8BS.js +0 -1
- package/dist/web/public/vendor/monaco/vs/dart-CmGfCvrO.js +0 -1
- package/dist/web/public/vendor/monaco/vs/dockerfile-CZqqYdch.js +0 -1
- package/dist/web/public/vendor/monaco/vs/ecl-30fUercY.js +0 -1
- package/dist/web/public/vendor/monaco/vs/editor/editor.main.css +0 -1
- package/dist/web/public/vendor/monaco/vs/editor/editor.main.js +0 -1
- package/dist/web/public/vendor/monaco/vs/editor/editor.worker.js +0 -1
- package/dist/web/public/vendor/monaco/vs/editor-KLE6jdfb.js +0 -831
- package/dist/web/public/vendor/monaco/vs/editor.js +0 -1
- package/dist/web/public/vendor/monaco/vs/editorWorkerHost-fVE1cjcC.js +0 -35
- package/dist/web/public/vendor/monaco/vs/elixir-xjPaIfzF.js +0 -1
- package/dist/web/public/vendor/monaco/vs/flow9-DqtmStfK.js +0 -1
- package/dist/web/public/vendor/monaco/vs/freemarker2-FWreY7v7.js +0 -3
- package/dist/web/public/vendor/monaco/vs/fsharp-BOMdg4U1.js +0 -1
- package/dist/web/public/vendor/monaco/vs/go-D_hbi-Jt.js +0 -1
- package/dist/web/public/vendor/monaco/vs/graphql-CKUU4kLG.js +0 -1
- package/dist/web/public/vendor/monaco/vs/handlebars-CZi4pHCH.js +0 -1
- package/dist/web/public/vendor/monaco/vs/hcl-DTaboeZW.js +0 -1
- package/dist/web/public/vendor/monaco/vs/html-j_7ZTNRU.js +0 -1
- package/dist/web/public/vendor/monaco/vs/html.worker-CA3iAimZ.js +0 -1
- package/dist/web/public/vendor/monaco/vs/htmlMode-27by_KM4.js +0 -1
- package/dist/web/public/vendor/monaco/vs/index-049xrXrp.js +0 -1
- package/dist/web/public/vendor/monaco/vs/index-CBVt3dzv.js +0 -5
- package/dist/web/public/vendor/monaco/vs/index.js +0 -1
- package/dist/web/public/vendor/monaco/vs/ini-CsNwO04R.js +0 -1
- package/dist/web/public/vendor/monaco/vs/initialize-DL0l1TGY.js +0 -1
- package/dist/web/public/vendor/monaco/vs/java-CI4ZMsH9.js +0 -1
- package/dist/web/public/vendor/monaco/vs/javascript-DTJl4Jn1.js +0 -1
- package/dist/web/public/vendor/monaco/vs/json.worker-BizpAl9O.js +0 -1
- package/dist/web/public/vendor/monaco/vs/jsonMode-DLUiMUrI.js +0 -1
- package/dist/web/public/vendor/monaco/vs/julia-BwzEvaQw.js +0 -1
- package/dist/web/public/vendor/monaco/vs/kotlin-IUYPiTV8.js +0 -1
- package/dist/web/public/vendor/monaco/vs/language/css/css.worker.js +0 -66
- package/dist/web/public/vendor/monaco/vs/language/css/monaco.contribution.js +0 -1
- package/dist/web/public/vendor/monaco/vs/language/html/html.worker.js +0 -443
- package/dist/web/public/vendor/monaco/vs/language/html/monaco.contribution.js +0 -1
- package/dist/web/public/vendor/monaco/vs/language/json/json.worker.js +0 -17
- package/dist/web/public/vendor/monaco/vs/language/json/monaco.contribution.js +0 -1
- package/dist/web/public/vendor/monaco/vs/language/typescript/monaco.contribution.js +0 -1
- package/dist/web/public/vendor/monaco/vs/language/typescript/ts.worker.js +0 -67693
- package/dist/web/public/vendor/monaco/vs/less-C0eDYdqa.js +0 -2
- package/dist/web/public/vendor/monaco/vs/lexon-iON-Kj97.js +0 -1
- package/dist/web/public/vendor/monaco/vs/liquid-CLJYelW6.js +0 -1
- package/dist/web/public/vendor/monaco/vs/loader.js +0 -1368
- package/dist/web/public/vendor/monaco/vs/lspLanguageFeatures-BIkJOWLw.js +0 -2
- package/dist/web/public/vendor/monaco/vs/lua-DtygF91M.js +0 -1
- package/dist/web/public/vendor/monaco/vs/m3-CsR4AuFi.js +0 -1
- package/dist/web/public/vendor/monaco/vs/main-BEx-Fmlo.js +0 -15
- package/dist/web/public/vendor/monaco/vs/main-DsK8pnKg.js +0 -3
- package/dist/web/public/vendor/monaco/vs/markdown-C_rD0bIw.js +0 -1
- package/dist/web/public/vendor/monaco/vs/mdx-CpzZIPHF.js +0 -1
- package/dist/web/public/vendor/monaco/vs/mips-CiYP61RB.js +0 -1
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-9cKT3C7t.js +0 -1
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-BE88ZNGY.js +0 -1
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-BPhsneLd.js +0 -1
- package/dist/web/public/vendor/monaco/vs/monaco.contribution-BgRy6xDf.js +0 -1
- package/dist/web/public/vendor/monaco/vs/msdax-C38-sJlp.js +0 -1
- package/dist/web/public/vendor/monaco/vs/mysql-CdtbpvbG.js +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/cs.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/cs.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/de.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/de.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/es.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/es.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/fr.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/fr.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/it.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/it.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/ja.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/ja.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/ko.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/ko.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/pl.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/pl.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/pt-br.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/pt-br.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/ru.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/ru.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/tr.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/tr.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-cn.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-cn.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-tw.d.ts +0 -1
- package/dist/web/public/vendor/monaco/vs/nls/lang/zh-tw.js +0 -9
- package/dist/web/public/vendor/monaco/vs/nls.messages-loader.js +0 -1
- package/dist/web/public/vendor/monaco/vs/objective-c-CntZFaHX.js +0 -1
- package/dist/web/public/vendor/monaco/vs/pascal-r6kuqfl_.js +0 -1
- package/dist/web/public/vendor/monaco/vs/pascaligo-BiXoTmXh.js +0 -1
- package/dist/web/public/vendor/monaco/vs/perl-DABw_TcH.js +0 -1
- package/dist/web/public/vendor/monaco/vs/pgsql-me_jFXeX.js +0 -1
- package/dist/web/public/vendor/monaco/vs/php-D_kh-9LK.js +0 -1
- package/dist/web/public/vendor/monaco/vs/pla-VfZjczW0.js +0 -1
- package/dist/web/public/vendor/monaco/vs/postiats-BBSzz8Pk.js +0 -1
- package/dist/web/public/vendor/monaco/vs/powerquery-Dt-g_2cc.js +0 -1
- package/dist/web/public/vendor/monaco/vs/powershell-B-7ap1zc.js +0 -1
- package/dist/web/public/vendor/monaco/vs/protobuf-BmtuEB1A.js +0 -2
- package/dist/web/public/vendor/monaco/vs/pug-BRpRNeEb.js +0 -1
- package/dist/web/public/vendor/monaco/vs/python-CqWUUgfu.js +0 -1
- package/dist/web/public/vendor/monaco/vs/qsharp-BzsFaUU9.js +0 -1
- package/dist/web/public/vendor/monaco/vs/r-f8dDdrp4.js +0 -1
- package/dist/web/public/vendor/monaco/vs/razor-hok5y9Km.js +0 -1
- package/dist/web/public/vendor/monaco/vs/redis-fvZQY4PI.js +0 -1
- package/dist/web/public/vendor/monaco/vs/redshift-45Et0LQi.js +0 -1
- package/dist/web/public/vendor/monaco/vs/restructuredtext-C7UUFKFD.js +0 -1
- package/dist/web/public/vendor/monaco/vs/ruby-CZO8zYTz.js +0 -1
- package/dist/web/public/vendor/monaco/vs/rust-Bfetafyc.js +0 -1
- package/dist/web/public/vendor/monaco/vs/sb-3GYllVck.js +0 -1
- package/dist/web/public/vendor/monaco/vs/scala-foMgrKo1.js +0 -1
- package/dist/web/public/vendor/monaco/vs/scheme-CHdMtr7p.js +0 -1
- package/dist/web/public/vendor/monaco/vs/scss-C1cmLt9V.js +0 -3
- package/dist/web/public/vendor/monaco/vs/shell-ClXCKCEW.js +0 -1
- package/dist/web/public/vendor/monaco/vs/solidity-MZ6ExpPy.js +0 -1
- package/dist/web/public/vendor/monaco/vs/sophia-DWkuSsPQ.js +0 -1
- package/dist/web/public/vendor/monaco/vs/sparql-AUGFYSyk.js +0 -1
- package/dist/web/public/vendor/monaco/vs/sql-32GpJSV2.js +0 -1
- package/dist/web/public/vendor/monaco/vs/st-CuDFIVZ_.js +0 -1
- package/dist/web/public/vendor/monaco/vs/swift-t-PfMj7W.js +0 -1
- package/dist/web/public/vendor/monaco/vs/systemverilog-Ch4vA8Yt.js +0 -1
- package/dist/web/public/vendor/monaco/vs/tcl-D74tq1nH.js +0 -1
- package/dist/web/public/vendor/monaco/vs/toggleHighContrast-qGX7E9o7.js +0 -61
- package/dist/web/public/vendor/monaco/vs/ts.worker-2QLmBukE.js +0 -1
- package/dist/web/public/vendor/monaco/vs/tsMode-B0S_kp2q.js +0 -11
- package/dist/web/public/vendor/monaco/vs/twig-C6taOxMV.js +0 -1
- package/dist/web/public/vendor/monaco/vs/typescript-_2t_511t.js +0 -1
- package/dist/web/public/vendor/monaco/vs/typespec-8OfoLt6R.js +0 -1
- package/dist/web/public/vendor/monaco/vs/vb-Dyb2648j.js +0 -1
- package/dist/web/public/vendor/monaco/vs/wgsl-BhLXMOR0.js +0 -298
- package/dist/web/public/vendor/monaco/vs/workers-BBttULjf.js +0 -1
- package/dist/web/public/vendor/monaco/vs/xml-cZjNDqDT.js +0 -1
- package/dist/web/public/vendor/monaco/vs/yaml-A1fOIdH6.js +0 -1
- package/dist/web/web-config.js +0 -143
- package/dist/web/web-config.js.map +0 -1
- package/docs/DEPLOYMENT.md +0 -382
- package/docs/MODEL_FINETUNING.md +0 -405
|
@@ -1,214 +1,214 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 工具函数模块:纯函数,无状态依赖,可被任意模块调用
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
function formatMarkdown(text) {
|
|
6
|
-
// 简单 Markdown 转换
|
|
7
|
-
return text
|
|
8
|
-
.replace(/&/g, '&')
|
|
9
|
-
.replace(/</g, '<')
|
|
10
|
-
.replace(/>/g, '>')
|
|
11
|
-
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
|
|
12
|
-
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
|
|
13
|
-
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
|
|
14
|
-
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
15
|
-
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
16
|
-
.replace(/`(.*?)`/g, '<code>$1</code>')
|
|
17
|
-
.replace(/\n/g, '<br>');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getDirectoryName(path) {
|
|
21
|
-
if (!path || path === '' || path === '/') return '/';
|
|
22
|
-
if (/^[A-Za-z]:$/.test(path)) return path + '\\';
|
|
23
|
-
if (/^[A-Za-z]:\\$/.test(path)) return path;
|
|
24
|
-
const parts = path.split(/[/\\]/).filter(Boolean);
|
|
25
|
-
if (parts.length <= 1) {
|
|
26
|
-
const driveMatch = parts[0].match(/^([A-Za-z]):$/);
|
|
27
|
-
if (driveMatch) return driveMatch[1] + ':\\';
|
|
28
|
-
return parts[0] || '/';
|
|
29
|
-
}
|
|
30
|
-
const parentParts = parts.slice(0, -1);
|
|
31
|
-
if (/^[A-Za-z]:$/.test(parentParts[0])) {
|
|
32
|
-
return parentParts[0] + '\\' + parentParts.slice(1).join('\\');
|
|
33
|
-
}
|
|
34
|
-
return parentParts.join('\\');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** 渲染空状态提示,减少重复的 innerHTML 赋值代码 */
|
|
38
|
-
function renderEmpty(el, text) {
|
|
39
|
-
if (el) el.innerHTML = '<div class="empty">' + (text || '') + '</div>';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function extractUrl(text) {
|
|
43
|
-
if (!text) return '';
|
|
44
|
-
const m = text.match(/(https?:\/\/[^\s]+)/);
|
|
45
|
-
return m ? m[1] : '';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function linkifyArtifacts(text) {
|
|
49
|
-
let html = escapeHtml(text);
|
|
50
|
-
// 占位符保护:先提取已识别的片段,避免后续正则把已插入的 <span> 标签二次包裹
|
|
51
|
-
const tokens = [];
|
|
52
|
-
const protect = (s) => { const k = '\u0000TOK' + tokens.length + '\u0000'; tokens.push(s); return k; };
|
|
53
|
-
// 1) URL:排除结尾标点与 HTML 实体
|
|
54
|
-
html = html.replace(/(https?:\/\/[^\s<>"'()]+)/g, (m) => protect('<span class="artifact" data-kind="url" data-path="' + m + '">' + m + '</span>'));
|
|
55
|
-
// 2) Windows 绝对路径(支持正/反斜杠、中文、空格,排除结尾标点)
|
|
56
|
-
html = html.replace(/([A-Za-z]:[\\\/][^\s<>"'()]+?)(?=[\s<>"',。;:、))]|$)/g, (m) => protect('<span class="artifact" data-kind="file" data-path="' + m + '">' + m + '</span>'));
|
|
57
|
-
// 3) Unix 风格绝对路径(至少两级目录,避免误匹配 "2023/2024"、"a/b" 等相对写法)
|
|
58
|
-
html = html.replace(/(\/(?:[A-Za-z0-9_\-.]+\/){1,}[A-Za-z0-9_\-.]*)/g, (m) => protect('<span class="artifact" data-kind="file" data-path="' + m + '">' + m + '</span>'));
|
|
59
|
-
tokens.forEach((s, i) => { html = html.split('\u0000TOK' + i + '\u0000').join(s); });
|
|
60
|
-
return html;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function formatSize(n) {
|
|
64
|
-
if (!Number.isFinite(n)) return '';
|
|
65
|
-
if (n < 1024) return n + ' B';
|
|
66
|
-
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
|
|
67
|
-
return (n / 1024 / 1024).toFixed(2) + ' MB';
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function estimateDataUrlSize(dataUrl) {
|
|
71
|
-
// base64 长度 × 0.75 估算字节
|
|
72
|
-
const b64 = (dataUrl || '').split(',')[1] || '';
|
|
73
|
-
return Math.round(b64.length * 0.75);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function formatToolArgs(args) {
|
|
77
|
-
if (!args || typeof args !== 'object') return '';
|
|
78
|
-
const keys = Object.keys(args);
|
|
79
|
-
if (!keys.length) return '';
|
|
80
|
-
const rows = keys.map(function (k) {
|
|
81
|
-
let v = args[k];
|
|
82
|
-
if (v == null) v = '';
|
|
83
|
-
else if (typeof v === 'object') v = JSON.stringify(v);
|
|
84
|
-
return '<div><span style="color:var(--ink-2,#6b7280);">' + escapeHtml(k) + ':</span> ' + escapeHtml(String(v)) + '</div>';
|
|
85
|
-
});
|
|
86
|
-
return '<div class="tc-args">' + rows.join('') + '</div>';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function renderMarkdown(text) {
|
|
90
|
-
let html = escapeHtml(text == null ? '' : text);
|
|
91
|
-
const codeBlocks = [];
|
|
92
|
-
html = html.replace(/```(\w*)\n?([\s\S]*?)```/g, function (_, lang, code) {
|
|
93
|
-
const idx = codeBlocks.length;
|
|
94
|
-
codeBlocks.push('<pre style="background:var(--bg,#f5f6fa);padding:10px;border-radius:8px;overflow-x:auto;font-size:12px;margin:6px 0;white-space:pre-wrap;word-break:break-word;"><code>' + code + '</code></pre>');
|
|
95
|
-
return '\x00CB' + idx + '\x00';
|
|
96
|
-
});
|
|
97
|
-
html = html.replace(/`([^`]+)`/g, '<code style="background:var(--bg,#f5f6fa);padding:1px 5px;border-radius:4px;font-size:12px;">$1</code>');
|
|
98
|
-
html = html.replace(/\*\*([^*]+)\*\*/g, '<b>$1</b>');
|
|
99
|
-
html = html.replace(/^### (.*)$/gm, '<div style="font-weight:700;margin:8px 0 4px;">$1</div>');
|
|
100
|
-
html = html.replace(/^## (.*)$/gm, '<div style="font-weight:700;font-size:15px;margin:10px 0 4px;">$1</div>');
|
|
101
|
-
html = html.replace(/^# (.*)$/gm, '<div style="font-weight:700;font-size:16px;margin:10px 0 4px;">$1</div>');
|
|
102
|
-
html = html.replace(/^[-*] (.*)$/gm, '<div style="padding-left:14px;">• $1</div>');
|
|
103
|
-
html = html.replace(/^(\d+)\. (.*)$/gm, '<div style="padding-left:20px;">$1. $2</div>');
|
|
104
|
-
html = html.replace(/(https?:\/\/[^\s<>"'()]+)/g, '<a href="$1" style="color:var(--brand,#4f6ef7);text-decoration:underline;" target="_blank">$1</a>');
|
|
105
|
-
html = html.replace(/\n/g, '<br>');
|
|
106
|
-
html = html.replace(/\x00CB(\d+)\x00/g, function (_, idx) { return codeBlocks[parseInt(idx)]; });
|
|
107
|
-
return html;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// 纯文本渲染:只转义HTML、换行和链接,不转标题/列表/加粗/代码块等格式
|
|
111
|
-
// 用于对话流展示,让输出像普通人聊天一样清爽
|
|
112
|
-
function renderPlainText(text) {
|
|
113
|
-
let html = escapeHtml(stripMarkdown(text == null ? '' : text));
|
|
114
|
-
html = html.replace(/(https?:\/\/[^\s<>"'()]+)/g, '<a href="$1" style="color:var(--brand,#4f6ef7);text-decoration:underline;" target="_blank">$1</a>');
|
|
115
|
-
html = html.replace(/\n/g, '<br>');
|
|
116
|
-
return html;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// 把 Markdown 格式符号全部去掉,只保留纯文本内容
|
|
120
|
-
function stripMarkdown(text) {
|
|
121
|
-
let s = String(text);
|
|
122
|
-
// 去掉代码块 ```...``` 里的标记,保留内容
|
|
123
|
-
s = s.replace(/```[\w]*\n?([\s\S]*?)```/g, '$1');
|
|
124
|
-
// 去掉行内代码 `code` 的反引号,保留内容
|
|
125
|
-
s = s.replace(/`([^`]+)`/g, '$1');
|
|
126
|
-
// 去掉加粗 **text** 和 __text__
|
|
127
|
-
s = s.replace(/\*\*([^*]+)\*\*/g, '$1');
|
|
128
|
-
s = s.replace(/__([^_]+)__/g, '$1');
|
|
129
|
-
// 去掉斜体 *text* 和 _text_(注意不要和列表冲突)
|
|
130
|
-
s = s.replace(/\*([^*\n]+)\*/g, '$1');
|
|
131
|
-
s = s.replace(/_([^_\n]+)_/g, '$1');
|
|
132
|
-
// 去掉标题 # ## ### 等,保留标题文字
|
|
133
|
-
s = s.replace(/^#{1,6}\s+/gm, '');
|
|
134
|
-
// 去掉无序列表符号 * - + (行首)
|
|
135
|
-
s = s.replace(/^[\s]*[-*+]\s+/gm, '');
|
|
136
|
-
// 去掉有序列表符号 1. 2. 等(行首)
|
|
137
|
-
s = s.replace(/^[\s]*\d+\.\s+/gm, '');
|
|
138
|
-
// 去掉引用符号 > (行首)
|
|
139
|
-
s = s.replace(/^[\s]*>\s?/gm, '');
|
|
140
|
-
// 去掉链接 [text](url) 只保留 text
|
|
141
|
-
s = s.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
|
142
|
-
// 去掉图片  只保留 alt
|
|
143
|
-
s = s.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1');
|
|
144
|
-
// 去掉分割线 --- *** ___
|
|
145
|
-
s = s.replace(/^[-*_]{3,}\s*$/gm, '');
|
|
146
|
-
// 去掉行号引用 L123、ln 456、第789行这种
|
|
147
|
-
s = s.replace(/\b[Ll][nN]?\s*\d+(-\d+)?/g, '');
|
|
148
|
-
s = s.replace(/第\d+行/g, '');
|
|
149
|
-
// 去掉多余的空行(连续3个以上换行变成2个)
|
|
150
|
-
s = s.replace(/\n{3,}/g, '\n\n');
|
|
151
|
-
return s.trim();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function statusBadge(s) { return '<span class="badge ' + s + '">' + s + '</span>'; }
|
|
155
|
-
|
|
156
|
-
function closeModal(id) { document.getElementById(id).classList.remove('show'); }
|
|
157
|
-
|
|
158
|
-
function openModal(id) { document.getElementById(id).classList.add('show'); }
|
|
159
|
-
|
|
160
|
-
function toast(msg) {
|
|
161
|
-
const el = document.getElementById('toast');
|
|
162
|
-
el.textContent = msg; el.classList.add('show');
|
|
163
|
-
clearTimeout(el._t); el._t = setTimeout(() => el.classList.remove('show'), 2200);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function fmtTime(iso) { try { return new Date(iso).toLocaleString(); } catch { return iso || ''; } }
|
|
167
|
-
|
|
168
|
-
function escapeHtml(s) { return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); }
|
|
169
|
-
|
|
170
|
-
function applyI18nToEl(el) {
|
|
171
|
-
const key = el.getAttribute('data-i18n');
|
|
172
|
-
if (!key) return;
|
|
173
|
-
const dict = I18N[currentLang] || I18N.zh;
|
|
174
|
-
const tag = el.tagName;
|
|
175
|
-
|
|
176
|
-
// 1) 输入类控件:只处理 placeholder,用 ph.<id> 专用键;缺失则保留中文原文
|
|
177
|
-
if (tag === 'INPUT' || tag === 'TEXTAREA') {
|
|
178
|
-
if (el._i18nPhZh === undefined) el._i18nPhZh = el.getAttribute('placeholder') || '';
|
|
179
|
-
const phKey = 'ph.' + (el.id || '');
|
|
180
|
-
if (dict[phKey] != null) el.placeholder = dict[phKey];
|
|
181
|
-
else el.placeholder = el._i18nPhZh;
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// 2) <select>:只翻 title,内容交给 renderModelSelect 等渲染函数管理
|
|
186
|
-
if (tag === 'SELECT') {
|
|
187
|
-
if (el._i18nTitleZh === undefined) el._i18nTitleZh = el.getAttribute('title') || '';
|
|
188
|
-
const tiKey = 'title.' + (el.id || '');
|
|
189
|
-
if (dict[tiKey] != null) el.title = dict[tiKey];
|
|
190
|
-
else el.title = el._i18nTitleZh;
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// 3) 含元素子节点:保留子元素,只替换 title 与直接文本节点
|
|
195
|
-
if (el.firstElementChild) {
|
|
196
|
-
if (el.hasAttribute('title')) el.title = t(key);
|
|
197
|
-
let done = false;
|
|
198
|
-
el.childNodes.forEach((node) => {
|
|
199
|
-
if (node.nodeType === 3 && node.nodeValue.trim()) {
|
|
200
|
-
node.nodeValue = done ? '' : t(key);
|
|
201
|
-
done = true;
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// 4) 纯文本元素:直接覆盖
|
|
208
|
-
el.textContent = t(key);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function t(key) {
|
|
212
|
-
const dict = I18N[currentLang] || I18N.zh;
|
|
213
|
-
return dict[key] || key;
|
|
214
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 工具函数模块:纯函数,无状态依赖,可被任意模块调用
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
function formatMarkdown(text) {
|
|
6
|
+
// 简单 Markdown 转换
|
|
7
|
+
return text
|
|
8
|
+
.replace(/&/g, '&')
|
|
9
|
+
.replace(/</g, '<')
|
|
10
|
+
.replace(/>/g, '>')
|
|
11
|
+
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
|
|
12
|
+
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
|
|
13
|
+
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
|
|
14
|
+
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
15
|
+
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
16
|
+
.replace(/`(.*?)`/g, '<code>$1</code>')
|
|
17
|
+
.replace(/\n/g, '<br>');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getDirectoryName(path) {
|
|
21
|
+
if (!path || path === '' || path === '/') return '/';
|
|
22
|
+
if (/^[A-Za-z]:$/.test(path)) return path + '\\';
|
|
23
|
+
if (/^[A-Za-z]:\\$/.test(path)) return path;
|
|
24
|
+
const parts = path.split(/[/\\]/).filter(Boolean);
|
|
25
|
+
if (parts.length <= 1) {
|
|
26
|
+
const driveMatch = parts[0].match(/^([A-Za-z]):$/);
|
|
27
|
+
if (driveMatch) return driveMatch[1] + ':\\';
|
|
28
|
+
return parts[0] || '/';
|
|
29
|
+
}
|
|
30
|
+
const parentParts = parts.slice(0, -1);
|
|
31
|
+
if (/^[A-Za-z]:$/.test(parentParts[0])) {
|
|
32
|
+
return parentParts[0] + '\\' + parentParts.slice(1).join('\\');
|
|
33
|
+
}
|
|
34
|
+
return parentParts.join('\\');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** 渲染空状态提示,减少重复的 innerHTML 赋值代码 */
|
|
38
|
+
function renderEmpty(el, text) {
|
|
39
|
+
if (el) el.innerHTML = '<div class="empty">' + (text || '') + '</div>';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function extractUrl(text) {
|
|
43
|
+
if (!text) return '';
|
|
44
|
+
const m = text.match(/(https?:\/\/[^\s]+)/);
|
|
45
|
+
return m ? m[1] : '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function linkifyArtifacts(text) {
|
|
49
|
+
let html = escapeHtml(text);
|
|
50
|
+
// 占位符保护:先提取已识别的片段,避免后续正则把已插入的 <span> 标签二次包裹
|
|
51
|
+
const tokens = [];
|
|
52
|
+
const protect = (s) => { const k = '\u0000TOK' + tokens.length + '\u0000'; tokens.push(s); return k; };
|
|
53
|
+
// 1) URL:排除结尾标点与 HTML 实体
|
|
54
|
+
html = html.replace(/(https?:\/\/[^\s<>"'()]+)/g, (m) => protect('<span class="artifact" data-kind="url" data-path="' + m + '">' + m + '</span>'));
|
|
55
|
+
// 2) Windows 绝对路径(支持正/反斜杠、中文、空格,排除结尾标点)
|
|
56
|
+
html = html.replace(/([A-Za-z]:[\\\/][^\s<>"'()]+?)(?=[\s<>"',。;:、))]|$)/g, (m) => protect('<span class="artifact" data-kind="file" data-path="' + m + '">' + m + '</span>'));
|
|
57
|
+
// 3) Unix 风格绝对路径(至少两级目录,避免误匹配 "2023/2024"、"a/b" 等相对写法)
|
|
58
|
+
html = html.replace(/(\/(?:[A-Za-z0-9_\-.]+\/){1,}[A-Za-z0-9_\-.]*)/g, (m) => protect('<span class="artifact" data-kind="file" data-path="' + m + '">' + m + '</span>'));
|
|
59
|
+
tokens.forEach((s, i) => { html = html.split('\u0000TOK' + i + '\u0000').join(s); });
|
|
60
|
+
return html;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function formatSize(n) {
|
|
64
|
+
if (!Number.isFinite(n)) return '';
|
|
65
|
+
if (n < 1024) return n + ' B';
|
|
66
|
+
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
|
|
67
|
+
return (n / 1024 / 1024).toFixed(2) + ' MB';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function estimateDataUrlSize(dataUrl) {
|
|
71
|
+
// base64 长度 × 0.75 估算字节
|
|
72
|
+
const b64 = (dataUrl || '').split(',')[1] || '';
|
|
73
|
+
return Math.round(b64.length * 0.75);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function formatToolArgs(args) {
|
|
77
|
+
if (!args || typeof args !== 'object') return '';
|
|
78
|
+
const keys = Object.keys(args);
|
|
79
|
+
if (!keys.length) return '';
|
|
80
|
+
const rows = keys.map(function (k) {
|
|
81
|
+
let v = args[k];
|
|
82
|
+
if (v == null) v = '';
|
|
83
|
+
else if (typeof v === 'object') v = JSON.stringify(v);
|
|
84
|
+
return '<div><span style="color:var(--ink-2,#6b7280);">' + escapeHtml(k) + ':</span> ' + escapeHtml(String(v)) + '</div>';
|
|
85
|
+
});
|
|
86
|
+
return '<div class="tc-args">' + rows.join('') + '</div>';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderMarkdown(text) {
|
|
90
|
+
let html = escapeHtml(text == null ? '' : text);
|
|
91
|
+
const codeBlocks = [];
|
|
92
|
+
html = html.replace(/```(\w*)\n?([\s\S]*?)```/g, function (_, lang, code) {
|
|
93
|
+
const idx = codeBlocks.length;
|
|
94
|
+
codeBlocks.push('<pre style="background:var(--bg,#f5f6fa);padding:10px;border-radius:8px;overflow-x:auto;font-size:12px;margin:6px 0;white-space:pre-wrap;word-break:break-word;"><code>' + code + '</code></pre>');
|
|
95
|
+
return '\x00CB' + idx + '\x00';
|
|
96
|
+
});
|
|
97
|
+
html = html.replace(/`([^`]+)`/g, '<code style="background:var(--bg,#f5f6fa);padding:1px 5px;border-radius:4px;font-size:12px;">$1</code>');
|
|
98
|
+
html = html.replace(/\*\*([^*]+)\*\*/g, '<b>$1</b>');
|
|
99
|
+
html = html.replace(/^### (.*)$/gm, '<div style="font-weight:700;margin:8px 0 4px;">$1</div>');
|
|
100
|
+
html = html.replace(/^## (.*)$/gm, '<div style="font-weight:700;font-size:15px;margin:10px 0 4px;">$1</div>');
|
|
101
|
+
html = html.replace(/^# (.*)$/gm, '<div style="font-weight:700;font-size:16px;margin:10px 0 4px;">$1</div>');
|
|
102
|
+
html = html.replace(/^[-*] (.*)$/gm, '<div style="padding-left:14px;">• $1</div>');
|
|
103
|
+
html = html.replace(/^(\d+)\. (.*)$/gm, '<div style="padding-left:20px;">$1. $2</div>');
|
|
104
|
+
html = html.replace(/(https?:\/\/[^\s<>"'()]+)/g, '<a href="$1" style="color:var(--brand,#4f6ef7);text-decoration:underline;" target="_blank">$1</a>');
|
|
105
|
+
html = html.replace(/\n/g, '<br>');
|
|
106
|
+
html = html.replace(/\x00CB(\d+)\x00/g, function (_, idx) { return codeBlocks[parseInt(idx)]; });
|
|
107
|
+
return html;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 纯文本渲染:只转义HTML、换行和链接,不转标题/列表/加粗/代码块等格式
|
|
111
|
+
// 用于对话流展示,让输出像普通人聊天一样清爽
|
|
112
|
+
function renderPlainText(text) {
|
|
113
|
+
let html = escapeHtml(stripMarkdown(text == null ? '' : text));
|
|
114
|
+
html = html.replace(/(https?:\/\/[^\s<>"'()]+)/g, '<a href="$1" style="color:var(--brand,#4f6ef7);text-decoration:underline;" target="_blank">$1</a>');
|
|
115
|
+
html = html.replace(/\n/g, '<br>');
|
|
116
|
+
return html;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 把 Markdown 格式符号全部去掉,只保留纯文本内容
|
|
120
|
+
function stripMarkdown(text) {
|
|
121
|
+
let s = String(text);
|
|
122
|
+
// 去掉代码块 ```...``` 里的标记,保留内容
|
|
123
|
+
s = s.replace(/```[\w]*\n?([\s\S]*?)```/g, '$1');
|
|
124
|
+
// 去掉行内代码 `code` 的反引号,保留内容
|
|
125
|
+
s = s.replace(/`([^`]+)`/g, '$1');
|
|
126
|
+
// 去掉加粗 **text** 和 __text__
|
|
127
|
+
s = s.replace(/\*\*([^*]+)\*\*/g, '$1');
|
|
128
|
+
s = s.replace(/__([^_]+)__/g, '$1');
|
|
129
|
+
// 去掉斜体 *text* 和 _text_(注意不要和列表冲突)
|
|
130
|
+
s = s.replace(/\*([^*\n]+)\*/g, '$1');
|
|
131
|
+
s = s.replace(/_([^_\n]+)_/g, '$1');
|
|
132
|
+
// 去掉标题 # ## ### 等,保留标题文字
|
|
133
|
+
s = s.replace(/^#{1,6}\s+/gm, '');
|
|
134
|
+
// 去掉无序列表符号 * - + (行首)
|
|
135
|
+
s = s.replace(/^[\s]*[-*+]\s+/gm, '');
|
|
136
|
+
// 去掉有序列表符号 1. 2. 等(行首)
|
|
137
|
+
s = s.replace(/^[\s]*\d+\.\s+/gm, '');
|
|
138
|
+
// 去掉引用符号 > (行首)
|
|
139
|
+
s = s.replace(/^[\s]*>\s?/gm, '');
|
|
140
|
+
// 去掉链接 [text](url) 只保留 text
|
|
141
|
+
s = s.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
|
142
|
+
// 去掉图片  只保留 alt
|
|
143
|
+
s = s.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1');
|
|
144
|
+
// 去掉分割线 --- *** ___
|
|
145
|
+
s = s.replace(/^[-*_]{3,}\s*$/gm, '');
|
|
146
|
+
// 去掉行号引用 L123、ln 456、第789行这种
|
|
147
|
+
s = s.replace(/\b[Ll][nN]?\s*\d+(-\d+)?/g, '');
|
|
148
|
+
s = s.replace(/第\d+行/g, '');
|
|
149
|
+
// 去掉多余的空行(连续3个以上换行变成2个)
|
|
150
|
+
s = s.replace(/\n{3,}/g, '\n\n');
|
|
151
|
+
return s.trim();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function statusBadge(s) { return '<span class="badge ' + s + '">' + s + '</span>'; }
|
|
155
|
+
|
|
156
|
+
function closeModal(id) { document.getElementById(id).classList.remove('show'); }
|
|
157
|
+
|
|
158
|
+
function openModal(id) { document.getElementById(id).classList.add('show'); }
|
|
159
|
+
|
|
160
|
+
function toast(msg) {
|
|
161
|
+
const el = document.getElementById('toast');
|
|
162
|
+
el.textContent = msg; el.classList.add('show');
|
|
163
|
+
clearTimeout(el._t); el._t = setTimeout(() => el.classList.remove('show'), 2200);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function fmtTime(iso) { try { return new Date(iso).toLocaleString(); } catch { return iso || ''; } }
|
|
167
|
+
|
|
168
|
+
function escapeHtml(s) { return String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); }
|
|
169
|
+
|
|
170
|
+
function applyI18nToEl(el) {
|
|
171
|
+
const key = el.getAttribute('data-i18n');
|
|
172
|
+
if (!key) return;
|
|
173
|
+
const dict = I18N[currentLang] || I18N.zh;
|
|
174
|
+
const tag = el.tagName;
|
|
175
|
+
|
|
176
|
+
// 1) 输入类控件:只处理 placeholder,用 ph.<id> 专用键;缺失则保留中文原文
|
|
177
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA') {
|
|
178
|
+
if (el._i18nPhZh === undefined) el._i18nPhZh = el.getAttribute('placeholder') || '';
|
|
179
|
+
const phKey = 'ph.' + (el.id || '');
|
|
180
|
+
if (dict[phKey] != null) el.placeholder = dict[phKey];
|
|
181
|
+
else el.placeholder = el._i18nPhZh;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 2) <select>:只翻 title,内容交给 renderModelSelect 等渲染函数管理
|
|
186
|
+
if (tag === 'SELECT') {
|
|
187
|
+
if (el._i18nTitleZh === undefined) el._i18nTitleZh = el.getAttribute('title') || '';
|
|
188
|
+
const tiKey = 'title.' + (el.id || '');
|
|
189
|
+
if (dict[tiKey] != null) el.title = dict[tiKey];
|
|
190
|
+
else el.title = el._i18nTitleZh;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 3) 含元素子节点:保留子元素,只替换 title 与直接文本节点
|
|
195
|
+
if (el.firstElementChild) {
|
|
196
|
+
if (el.hasAttribute('title')) el.title = t(key);
|
|
197
|
+
let done = false;
|
|
198
|
+
el.childNodes.forEach((node) => {
|
|
199
|
+
if (node.nodeType === 3 && node.nodeValue.trim()) {
|
|
200
|
+
node.nodeValue = done ? '' : t(key);
|
|
201
|
+
done = true;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 4) 纯文本元素:直接覆盖
|
|
208
|
+
el.textContent = t(key);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function t(key) {
|
|
212
|
+
const dict = I18N[currentLang] || I18N.zh;
|
|
213
|
+
return dict[key] || key;
|
|
214
|
+
}
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "飞虹 Code",
|
|
3
|
-
"short_name": "飞虹Code",
|
|
4
|
-
"description": "终端 AI 编程智能体,对标 Muse Code / Cursor",
|
|
5
|
-
"start_url": "/",
|
|
6
|
-
"display": "standalone",
|
|
7
|
-
"background_color": "#1a1a2e",
|
|
8
|
-
"theme_color": "#6366f1",
|
|
9
|
-
"orientation": "any",
|
|
10
|
-
"scope": "/",
|
|
11
|
-
"lang": "zh-CN",
|
|
12
|
-
"dir": "ltr",
|
|
13
|
-
"categories": ["developer-tools", "productivity", "utilities"],
|
|
14
|
-
"icons": [
|
|
15
|
-
{
|
|
16
|
-
"src": "/icons/icon-192.png",
|
|
17
|
-
"sizes": "192x192",
|
|
18
|
-
"type": "image/png",
|
|
19
|
-
"purpose": "any maskable"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"src": "/icons/icon-512.png",
|
|
23
|
-
"sizes": "512x512",
|
|
24
|
-
"type": "image/png",
|
|
25
|
-
"purpose": "any maskable"
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"shortcuts": [
|
|
29
|
-
{
|
|
30
|
-
"name": "新建任务",
|
|
31
|
-
"short_name": "新任务",
|
|
32
|
-
"description": "创建新的 AI 编程任务",
|
|
33
|
-
"url": "/?action=new-task",
|
|
34
|
-
"icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }]
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"name": "代码补全",
|
|
38
|
-
"short_name": "补全",
|
|
39
|
-
"description": "快速代码补全",
|
|
40
|
-
"url": "/?action=completion",
|
|
41
|
-
"icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }]
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"name": "代码审查",
|
|
45
|
-
"short_name": "审查",
|
|
46
|
-
"description": "AI 代码审查",
|
|
47
|
-
"url": "/?action=review",
|
|
48
|
-
"icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }]
|
|
49
|
-
}
|
|
50
|
-
],
|
|
51
|
-
"prefer_related_applications": false,
|
|
52
|
-
"features": [
|
|
53
|
-
"Cross Platform",
|
|
54
|
-
"AI Code Completion",
|
|
55
|
-
"Multi-agent Collaboration",
|
|
56
|
-
"Voice Programming",
|
|
57
|
-
"Knowledge Library"
|
|
58
|
-
]
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "飞虹 Code",
|
|
3
|
+
"short_name": "飞虹Code",
|
|
4
|
+
"description": "终端 AI 编程智能体,对标 Muse Code / Cursor",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#1a1a2e",
|
|
8
|
+
"theme_color": "#6366f1",
|
|
9
|
+
"orientation": "any",
|
|
10
|
+
"scope": "/",
|
|
11
|
+
"lang": "zh-CN",
|
|
12
|
+
"dir": "ltr",
|
|
13
|
+
"categories": ["developer-tools", "productivity", "utilities"],
|
|
14
|
+
"icons": [
|
|
15
|
+
{
|
|
16
|
+
"src": "/icons/icon-192.png",
|
|
17
|
+
"sizes": "192x192",
|
|
18
|
+
"type": "image/png",
|
|
19
|
+
"purpose": "any maskable"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"src": "/icons/icon-512.png",
|
|
23
|
+
"sizes": "512x512",
|
|
24
|
+
"type": "image/png",
|
|
25
|
+
"purpose": "any maskable"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"shortcuts": [
|
|
29
|
+
{
|
|
30
|
+
"name": "新建任务",
|
|
31
|
+
"short_name": "新任务",
|
|
32
|
+
"description": "创建新的 AI 编程任务",
|
|
33
|
+
"url": "/?action=new-task",
|
|
34
|
+
"icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "代码补全",
|
|
38
|
+
"short_name": "补全",
|
|
39
|
+
"description": "快速代码补全",
|
|
40
|
+
"url": "/?action=completion",
|
|
41
|
+
"icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "代码审查",
|
|
45
|
+
"short_name": "审查",
|
|
46
|
+
"description": "AI 代码审查",
|
|
47
|
+
"url": "/?action=review",
|
|
48
|
+
"icons": [{ "src": "/icons/icon-192.png", "sizes": "192x192" }]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"prefer_related_applications": false,
|
|
52
|
+
"features": [
|
|
53
|
+
"Cross Platform",
|
|
54
|
+
"AI Code Completion",
|
|
55
|
+
"Multi-agent Collaboration",
|
|
56
|
+
"Voice Programming",
|
|
57
|
+
"Knowledge Library"
|
|
58
|
+
]
|
|
59
|
+
}
|