Normalize fullwidth punctuation to ASCII across codebase.

Add scripts/normalize_ambiguous_unicode.py; fix corrupted patch_instance_theme_templates.py. Preserves curly quotes in string literals; removes Git homoglyph warnings on .env.example.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-08 23:42:26 +08:00
parent aaa72c7961
commit b733e551a0
392 changed files with 71522 additions and 71369 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+11 -11
View File
@@ -1,5 +1,5 @@
/**
* 内照明心复盘语录 + 当日交易记录 + 按需 K 线
* 内照明心:复盘语录 + 当日交易记录 + 按需 K 线.
*/
(function () {
const page = document.getElementById("page-archive");
@@ -597,7 +597,7 @@
function quotePreview(text) {
const s = String(text || "").replace(/\s+/g, " ").trim();
if (!s) return "(空)";
if (!s) return "(空)";
return s.length > 36 ? s.slice(0, 36) + "…" : s;
}
@@ -649,7 +649,7 @@
elQuotesCount.textContent = quotes.length ? quotes.length + " 条" : "";
}
if (!quotes.length) {
elQuotesList.innerHTML = '<p class="archive-empty">暂无复盘语录可在上方添加</p>';
elQuotesList.innerHTML = '<p class="archive-empty">暂无复盘语录,可在上方添加.</p>';
return;
}
elQuotesList.innerHTML = quotes
@@ -675,7 +675,7 @@
(selected
? '<div class="archive-quote-detail">' +
'<div class="archive-quote-full">' +
esc(q.content || "(空)") +
esc(q.content || "(空)") +
"</div>" +
'<div class="archive-quote-actions">' +
'<button type="button" class="ghost archive-quote-edit-btn" data-id="' +
@@ -778,7 +778,7 @@
const q = findQuote(quoteId);
const content = q && String(q.content || "").trim();
if (!q || !content) {
setStatus("语录内容为空无法发起 AI 对话");
setStatus("语录内容为空,无法发起 AI 对话");
return;
}
try {
@@ -801,7 +801,7 @@
}
async function deleteQuote(id) {
if (!id || !window.confirm("确定删除这条复盘语录")) return;
if (!id || !window.confirm("确定删除这条复盘语录?")) return;
const r = await apiFetch("/api/archive/quotes/" + id, { method: "DELETE" });
if (!r.ok) {
const j = await r.json().catch(function () {
@@ -1170,7 +1170,7 @@
const exKey = String(tr.exchange_key || "").toLowerCase();
const sym = tr.symbol || "";
if (!exKey || !sym) {
setStatus("该笔交易缺少交易所或合约无法切换");
setStatus("该笔交易缺少交易所或合约,无法切换");
return;
}
const key = tradeRowKey(tr);
@@ -1206,7 +1206,7 @@
const exKey = String(tr.exchange_key || "").toLowerCase();
const sym = tr.symbol || "";
if (!exKey || !sym) {
setStatus("该笔交易缺少交易所或合约无法加载图表");
setStatus("该笔交易缺少交易所或合约,无法加载图表");
return;
}
setChartOpen(true);
@@ -1217,7 +1217,7 @@
if (!elTrades) return;
if (!dailyTrades.length) {
elTrades.innerHTML =
'<p class="archive-empty">该日暂无交易记录可调整日期或点击「同步」拉取数据</p>';
'<p class="archive-empty">该日暂无交易记录.可调整日期或点击「同步」拉取数据.</p>';
return;
}
elTrades.innerHTML =
@@ -1390,7 +1390,7 @@
async function deleteTrade(tradeId, exchangeKey) {
const exKey = exchangeKey || (selected && selected.exchange_key);
if (!exKey || tradeId == null) return;
if (!window.confirm("从档案移除该笔交易?(不影响交易所实例里的复盘记录")) return;
if (!window.confirm("从档案移除该笔交易?(不影响交易所实例里的复盘记录)")) return;
const r = await apiFetch("/api/archive/trade/" + exKey + "/" + tradeId, { method: "DELETE" });
if (!r.ok) {
const j = await r.json().catch(function () {
@@ -1510,7 +1510,7 @@
}
async function syncAll() {
setStatus("同步中可能需数分钟…");
setStatus("同步中(可能需数分钟)…");
if (elBtnSync) elBtnSync.disabled = true;
try {
const r = await apiFetch("/api/archive/sync", { method: "POST" });
+11 -11
View File
@@ -61,15 +61,15 @@
const state = (status && status.state) || {};
const root = (status && status.backup_root) || "";
let html = '<div class="backup-meta">';
html += '<div>目录<code>' + esc(root) + '</code></div>';
html += '<div>目录:<code>' + esc(root) + '</code></div>';
if (state.last_backup_at) {
html += '<div>上次备份' + esc(state.last_backup_at) + '' + esc(state.last_trigger || "") + "</div>";
html += '<div>上次备份:' + esc(state.last_backup_at) + '(' + esc(state.last_trigger || "") + ")</div>";
}
if (state.last_auto_at) {
html += '<div>上次自动' + esc(state.last_auto_at) + "</div>";
html += '<div>上次自动:' + esc(state.last_auto_at) + "</div>";
}
if (state.last_restore_at) {
html += '<div>上次恢复' + esc(state.last_restore_at) + " ← " + esc(state.last_restore_from || "") + "</div>";
html += '<div>上次恢复:' + esc(state.last_restore_at) + " ← " + esc(state.last_restore_from || "") + "</div>";
}
html += "</div>";
if (!rows.length) {
@@ -159,14 +159,14 @@
setStatus(data.detail || "备份失败", true);
return;
}
setStatus("完成" + (data.file || "") + "" + fmtBytes(data.size) + "");
setStatus("完成:" + (data.file || "") + "(" + fmtBytes(data.size) + ")");
await loadBackupStatus();
if (typeof showToast === "function") showToast("备份完成");
}
async function restoreLocal(name) {
if (!name) return;
if (!window.confirm("确认从服务器备份 " + name + " 恢复\n恢复前会自动做 pre-restore 快照并重启 PM2")) return;
if (!window.confirm("确认从服务器备份 " + name + " 恢复?\n恢复前会自动做 pre-restore 快照并重启 PM2.")) return;
if (window.prompt('请输入 RESTORE 确认恢复') !== "RESTORE") return;
setStatus("恢复中…");
const r = await fetch("/api/backup/restore-local", {
@@ -182,9 +182,9 @@
setStatus(data.detail || "恢复失败", true);
return;
}
setStatus("恢复完成已恢复 " + ((data.restored && data.restored.length) || 0) + " 个文件");
setStatus("恢复完成,已恢复 " + ((data.restored && data.restored.length) || 0) + " 个文件");
await loadBackupStatus();
if (typeof showToast === "function") showToast("恢复完成请刷新页面");
if (typeof showToast === "function") showToast("恢复完成,请刷新页面");
}
async function restoreUpload() {
@@ -193,7 +193,7 @@
setStatus("请选择 .zip 备份文件", true);
return;
}
if (!window.confirm("确认上传并恢复 " + file.name + "\n恢复前会自动做 pre-restore 快照并重启 PM2")) return;
if (!window.confirm("确认上传并恢复 " + file.name + "?\n恢复前会自动做 pre-restore 快照并重启 PM2.")) return;
if (window.prompt('请输入 RESTORE 确认恢复') !== "RESTORE") return;
setStatus("上传并恢复中…");
const fd = new FormData();
@@ -211,10 +211,10 @@
setStatus(data.detail || "恢复失败", true);
return;
}
setStatus("恢复完成已恢复 " + ((data.restored && data.restored.length) || 0) + " 个文件");
setStatus("恢复完成,已恢复 " + ((data.restored && data.restored.length) || 0) + " 个文件");
if (elRestoreFile) elRestoreFile.value = "";
await loadBackupStatus();
if (typeof showToast === "function") showToast("恢复完成请刷新页面");
if (typeof showToast === "function") showToast("恢复完成,请刷新页面");
}
window.initBackupSettingsUI = async function () {
+3 -3
View File
@@ -1,5 +1,5 @@
/**
* 中控策略计算器趋势回调 / 滚仓历史测算
* 中控策略计算器:趋势回调 / 滚仓历史测算
*/
(function () {
const page = document.getElementById("page-calculator");
@@ -42,7 +42,7 @@
return String(n);
}
/** 去掉尾部多余 0用于乘数/精度展示 */
/** 去掉尾部多余 0,用于乘数/精度展示 */
function fmtTrim(v, maxDigits) {
if (v == null || v === "") return "—";
const n = Number(v);
@@ -376,7 +376,7 @@
"<div><span>单次风险预算</span><strong>" +
fmt(data.risk_budget_u, 2) +
"U</strong></div>" +
"<div><span>首仓张数自动</span><strong>" +
"<div><span>首仓张数(自动)</span><strong>" +
fmt(data.first_contracts, amt) +
"</strong></div>" +
"<div><span>最终累计张数</span><strong>" +
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* 行情区左侧画线工具canvas 叠加层坐标与 Lightweight Charts 对齐
* 行情区左侧画线工具(canvas 叠加层,坐标与 Lightweight Charts 对齐).
*/
(function () {
const STORAGE_PREFIX = "hubMarketDraw:";
@@ -1276,7 +1276,7 @@
function setActiveTool(tool) {
if (!TOOL_LABELS[tool]) return;
if (tool === "clear") {
if (drawings.length && window.confirm("清除当前图表上的全部画线")) {
if (drawings.length && window.confirm("清除当前图表上的全部画线?")) {
drawings = [];
selectedId = null;
draft = null;
+1 -1
View File
@@ -1,4 +1,4 @@
/* 数据看板 — 随中控亮/暗主题卡片柔光 */
/* 数据看板 — 随中控亮/暗主题,卡片柔光 */
body.hub-page-dashboard {
--dash-card-bg: var(--panel);
--dash-card-border: var(--border-soft);
+4 -4
View File
@@ -1,5 +1,5 @@
/**
* 中控数据看板后端 SSE 推送版本号前端拉快照刷新无轮询闪烁
* 中控数据看板:后端 SSE 推送版本号,前端拉快照刷新(无轮询闪烁).
*/
(function () {
const page = document.getElementById("page-dashboard");
@@ -362,9 +362,9 @@
const names = (data.accounts || [])
.filter((a) => a.loss_alert)
.map((a) => a.name)
.join("");
.join(",");
elBanner.classList.add("is-on");
elBannerText.textContent = `${alertCount} 户单日平仓亏损超过资金合计 ${threshold}%${names}`;
elBannerText.textContent = `${alertCount} 户单日平仓亏损超过资金合计 ${threshold}%:${names}`;
} else {
elBanner.classList.remove("is-on");
elBannerText.textContent = "";
@@ -426,7 +426,7 @@
});
dashEventSource.onerror = () => {
closeDashboardStream();
setStatus("SSE 断开8s 后重连…", true);
setStatus("SSE 断开,8s 后重连…", true);
dashReconnectTimer = setTimeout(() => {
if (inited) {
connectDashboardStream();
+7 -7
View File
@@ -1,5 +1,5 @@
/**
* 中控资金概况总资金曲线分户资金与回撤资金户+交易户不含浮盈
* 中控资金概况:总资金曲线,分户资金与回撤(资金户+交易户,不含浮盈).
*/
(function () {
const page = document.getElementById("page-funds");
@@ -201,7 +201,7 @@
'"' +
clickable +
' title="' +
esc(monitored ? "点击查看 " + name + " 资金曲线" : "未监控不参与合计") +
esc(monitored ? "点击查看 " + name + " 资金曲线" : "未监控,不参与合计") +
'">' +
'<div class="funds-ac-head">' +
'<h3 class="funds-ac-name">' +
@@ -279,7 +279,7 @@
if (elFsTitle) elFsTitle.textContent = ac.name || ac.key || "—";
if (elFsSub) {
const parts = [
"资金户 + 交易户 + 期权户USDC≈USDT不含浮盈",
"资金户 + 交易户 + 期权户(USDC≈USDT,不含浮盈)",
"交易日 " + (meta.trading_day || "—"),
"自 " + (meta.history_start_day || "—") + " 起",
];
@@ -330,7 +330,7 @@
});
} else if (elFsChartHost) {
elFsChartHost.innerHTML =
'<p class="funds-empty">暂无历史曲线请保持监控板运行以积累快照</p>';
'<p class="funds-empty">暂无历史曲线,请保持监控板运行以积累快照</p>';
}
}
@@ -340,13 +340,13 @@
const hour = data && data.reset_hour != null ? data.reset_hour : 8;
if (elDescBody) {
elDescBody.textContent =
"总资金 = 各监控户永续资金账户 + 交易账户 + 期权账户USDC 按 1:1 计入 USDT);自 " +
"总资金 = 各监控户(永续资金账户 + 交易账户 + 期权账户,USDC 按 1:1 计入 USDT);自 " +
start +
" 起按北京时间 " +
hour +
":00 交易日切日快照最多保留 " +
":00 交易日切日快照,最多保留 " +
keep +
" 天起算日由环境变量 HUB_FUND_HISTORY_START_DAY 配置";
" 天.起算日由环境变量 HUB_FUND_HISTORY_START_DAY 配置.";
}
if (elChartSub) {
elChartSub.textContent = keep + " TRADING DAYS";
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -51,7 +51,7 @@
<p id="login-err" class="login-err" hidden></p>
<p id="login-hint" class="login-foot" hidden></p>
</form>
<p class="login-foot">默认账号 <code>admin</code> / <code>admin123</code>可在 hub <code>.env</code> 修改 <code>HUB_USERNAME</code> / <code>HUB_PASSWORD</code></p>
<p class="login-foot">默认账号 <code>admin</code> / <code>admin123</code>;可在 hub <code>.env</code> 修改 <code>HUB_USERNAME</code> / <code>HUB_PASSWORD</code></p>
</div>
<script>
(function () {
@@ -68,8 +68,8 @@
if (inFrame) {
hint.hidden = false;
hint.textContent = isHttps
? "嵌入模式登录成功后将自动写入会话"
: "嵌入模式需 HTTPS 中控HTTP 时请用本地导航工具栏「中控登录」";
? "嵌入模式:登录成功后将自动写入会话."
: "嵌入模式需 HTTPS 中控;HTTP 时请用本地导航工具栏「中控登录」.";
}
function showErr(msg) {
@@ -81,11 +81,11 @@
const target = dest.startsWith("/") ? dest : "/monitor";
if (inFrame) {
if (!token) {
showErr("登录响应缺少 session_token请升级云端 hub 或使用本地导航「中控登录」");
showErr("登录响应缺少 session_token,请升级云端 hub 或使用本地导航「中控登录」.");
return;
}
if (!isHttps) {
showErr("跨站 iframe 登录需要 HTTPS 中控请改用本地导航「中控登录」按钮");
showErr("跨站 iframe 登录需要 HTTPS 中控;请改用本地导航「中控登录」按钮.");
return;
}
const q = new URLSearchParams({ token, next: target });
@@ -147,12 +147,12 @@
return;
}
if (r.status === 403) {
showErr("访问被拒绝(403)云端 hub 需设置 HUB_ALLOW_PUBLIC=true");
showErr("访问被拒绝(403):云端 hub 需设置 HUB_ALLOW_PUBLIC=true");
} else {
showErr(j.detail || j.msg || "用户名或密码错误 (" + r.status + ")");
}
} catch (ex) {
showErr("网络错误" + ex);
showErr("网络错误:" + ex);
}
submitBtn.disabled = false;
submitBtn.textContent = oldLabel;
+5 -5
View File
@@ -1,5 +1,5 @@
/**
* 开仓计划新建 / 进行中 / 历史 / 胜率统计
* 开仓计划:新建 / 进行中 / 历史 / 胜率统计
*/
(function () {
const page = document.getElementById("page-plan");
@@ -217,7 +217,7 @@
'<label class="plan-field plan-field-inline"><span>盈亏</span>' +
'<input class="plan-close-pnl" data-id="' +
esc(p.id) +
'" type="number" step="any" placeholder="U可选" value="' +
'" type="number" step="any" placeholder="U(可选)" value="' +
(p.pnl_amount != null ? esc(p.pnl_amount) : "") +
'" /></label>' +
'<button type="button" class="primary plan-btn-archive" data-id="' +
@@ -584,7 +584,7 @@
const id = t.getAttribute("data-id");
if (!id) return;
if (t.classList.contains("plan-btn-del")) {
if (!window.confirm("确定删除该进行中的计划")) return;
if (!window.confirm("确定删除该进行中的计划?")) return;
api("/api/entry-plans/" + id, { method: "DELETE" })
.then(function () {
toast("已删除");
@@ -609,12 +609,12 @@
const schemeEl = card && card.querySelector('.plan-active-scheme[data-id="' + id + '"]');
const result = resultEl && resultEl.value;
if (!result) {
toast("请先选择结果盈/亏", true);
toast("请先选择结果(盈/亏)", true);
return;
}
const scheme = schemeEl && schemeEl.value;
if (!scheme) {
toast("请先选择入场方案根据实际进场填写", true);
toast("请先选择入场方案(根据实际进场填写)", true);
return;
}
const payload = { result: result, entry_scheme: scheme };
+2 -2
View File
@@ -1,5 +1,5 @@
/**
* 策略说明三所 MD + 开仓检查清单 JSON
* 策略说明:三所 MD + 开仓检查清单 JSON.
*/
(function () {
const page = document.getElementById("page-strategy");
@@ -109,7 +109,7 @@
if (docBody) docBody.innerHTML = data.strategy_html || "";
if (docSource) {
const ver = data.version ? ` · ${data.version}` : "";
docSource.textContent = `文档${data.md_source || ""}${ver}`;
docSource.textContent = `文档:${data.md_source || ""}${ver}`;
}
renderChecklist(data.checklist);
scheduleHeightSync();
+1 -1
View File
@@ -1,4 +1,4 @@
/** 中控主题暗色默认/ 亮色localStorage hub-theme */
/** 中控主题:暗色(默认)/ 亮色,localStorage hub-theme */
(function (global) {
const KEY = "hub-theme";
const META = { dark: "#0b0e18", light: "#d4dde8" };
+1 -1
View File
@@ -1,5 +1,5 @@
/**
* 时间平仓 + 整点强制清仓表单开关 + 持仓/顶栏倒计时
* 时间平仓 + 整点强制清仓:表单开关 + 持仓/顶栏倒计时.
*/
(function (global) {
"use strict";