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:
+8323
-8323
File diff suppressed because it is too large
Load Diff
+5581
-5581
File diff suppressed because it is too large
Load Diff
@@ -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" });
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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>" +
|
||||
|
||||
+3546
-3546
File diff suppressed because it is too large
Load Diff
@@ -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,4 +1,4 @@
|
||||
/* 数据看板 — 随中控亮/暗主题,卡片柔光 */
|
||||
/* 数据看板 — 随中控亮/暗主题,卡片柔光 */
|
||||
body.hub-page-dashboard {
|
||||
--dash-card-bg: var(--panel);
|
||||
--dash-card-border: var(--border-soft);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
+1197
-1197
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
/** 中控主题:暗色(默认)/ 亮色,localStorage hub-theme */
|
||||
/** 中控主题:暗色(默认)/ 亮色,localStorage hub-theme */
|
||||
(function (global) {
|
||||
const KEY = "hub-theme";
|
||||
const META = { dark: "#0b0e18", light: "#d4dde8" };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* 时间平仓 + 整点强制清仓:表单开关 + 持仓/顶栏倒计时。
|
||||
* 时间平仓 + 整点强制清仓:表单开关 + 持仓/顶栏倒计时.
|
||||
*/
|
||||
(function (global) {
|
||||
"use strict";
|
||||
|
||||
Reference in New Issue
Block a user