Add instance system guide nav (default off) with overview/options/hedge manual.
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,6 +14,7 @@ DEFAULT_INSTANCE_DISPLAY: dict[str, bool] = {
|
||||
"show_nav_records": True,
|
||||
"show_nav_stats": True,
|
||||
"show_nav_risk_policy": True,
|
||||
"show_nav_system_guide": False,
|
||||
"show_nav_env_config": True,
|
||||
"show_nav_options": True,
|
||||
"show_nav_options_review": True,
|
||||
@@ -32,6 +33,7 @@ DISPLAY_LABELS: dict[str, str] = {
|
||||
"show_nav_records": "交易记录与复盘",
|
||||
"show_nav_stats": "统计分析",
|
||||
"show_nav_risk_policy": "风控说明",
|
||||
"show_nav_system_guide": "系统说明",
|
||||
"show_nav_env_config": "env配置",
|
||||
"show_nav_options": "期权",
|
||||
"show_nav_options_review": "期权复盘",
|
||||
@@ -50,6 +52,7 @@ NAV_TAB_ALLOWED: dict[str, str] = {
|
||||
"records": "show_nav_records",
|
||||
"stats": "show_nav_stats",
|
||||
"risk_policy": "show_nav_risk_policy",
|
||||
"system_guide": "show_nav_system_guide",
|
||||
"env_config": "show_nav_env_config",
|
||||
"options": "show_nav_options",
|
||||
"options_review": "show_nav_options_review",
|
||||
@@ -112,6 +115,7 @@ def display_meta_for_ui() -> list[dict[str, Any]]:
|
||||
"show_nav_records",
|
||||
"show_nav_stats",
|
||||
"show_nav_risk_policy",
|
||||
"show_nav_system_guide",
|
||||
"show_nav_env_config",
|
||||
"show_nav_options",
|
||||
"show_nav_options_review",
|
||||
|
||||
@@ -22,6 +22,7 @@ EMBED_TABS: tuple[str, ...] = (
|
||||
"records",
|
||||
"stats",
|
||||
"risk_policy",
|
||||
"system_guide",
|
||||
"env_config",
|
||||
"settings",
|
||||
)
|
||||
@@ -41,6 +42,7 @@ PATH_TO_EMBED_TAB: dict[str, str] = {
|
||||
"/records": "records",
|
||||
"/stats": "stats",
|
||||
"/risk_policy": "risk_policy",
|
||||
"/system_guide": "system_guide",
|
||||
"/env_config": "env_config",
|
||||
"/settings": "settings",
|
||||
}
|
||||
|
||||
@@ -202,6 +202,10 @@ def build_settings_tabs(display: dict[str, Any] | None, instance_settings: dict[
|
||||
|
||||
def settings_page_context(page: str, *, instance_base_dir: str | None = None, **kwargs: Any) -> dict[str, Any]:
|
||||
p = (page or "").strip()
|
||||
if p == "system_guide":
|
||||
from lib.instance.instance_system_guide_lib import system_guide_template_context
|
||||
|
||||
return system_guide_template_context()
|
||||
if p not in ("settings", "risk_policy", "env_config"):
|
||||
return {}
|
||||
display = kwargs.pop("display", None)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
"""实例「系统说明」:加载 Markdown,生成 h2 目录与带锚点正文."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from html import escape
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from lib.hub.hub_strategy_lib import render_markdown_html
|
||||
from lib.paths import REPO_ROOT
|
||||
|
||||
|
||||
def system_guide_md_path() -> Path:
|
||||
return REPO_ROOT / "docs" / "系统说明.md"
|
||||
|
||||
|
||||
def _slugify(text: str) -> str:
|
||||
raw = re.sub(r"<[^>]+>", "", text or "")
|
||||
raw = re.sub(r"\s+", "-", raw.strip())
|
||||
raw = re.sub(r"[^\w\u4e00-\u9fff\-]+", "", raw)
|
||||
return raw[:80] or "section"
|
||||
|
||||
|
||||
def _inject_h2_ids(html: str) -> tuple[str, list[dict[str, str]]]:
|
||||
"""为 h2 注入 id,并收集目录(仅 h2)."""
|
||||
toc: list[dict[str, str]] = []
|
||||
used: dict[str, int] = {}
|
||||
|
||||
def repl(m: re.Match[str]) -> str:
|
||||
inner = m.group(1)
|
||||
base = _slugify(inner)
|
||||
n = used.get(base, 0) + 1
|
||||
used[base] = n
|
||||
hid = base if n == 1 else f"{base}-{n}"
|
||||
toc.append({"id": hid, "title": re.sub(r"<[^>]+>", "", inner).strip()})
|
||||
return f'<h2 id="{escape(hid)}">{inner}</h2>'
|
||||
|
||||
out = re.sub(r"<h2>(.*?)</h2>", repl, html, flags=re.I | re.S)
|
||||
return out, toc
|
||||
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def _load_payload_cached(mtime_ns: int, path_str: str) -> dict[str, Any]:
|
||||
path = Path(path_str)
|
||||
try:
|
||||
md_text = path.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
md_text = "# 系统说明缺失\n\n未找到 `docs/系统说明.md`。"
|
||||
body = render_markdown_html(md_text)
|
||||
body, toc = _inject_h2_ids(body)
|
||||
return {"html": body, "toc": toc, "mtime_ns": mtime_ns}
|
||||
|
||||
|
||||
def load_system_guide_payload() -> dict[str, Any]:
|
||||
path = system_guide_md_path()
|
||||
try:
|
||||
mtime_ns = path.stat().st_mtime_ns
|
||||
except OSError:
|
||||
mtime_ns = 0
|
||||
return dict(_load_payload_cached(mtime_ns, str(path)))
|
||||
|
||||
|
||||
def system_guide_template_context() -> dict[str, Any]:
|
||||
payload = load_system_guide_payload()
|
||||
return {
|
||||
"system_guide_html": payload.get("html") or "",
|
||||
"system_guide_toc": payload.get("toc") or [],
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="display-prefs-checks">
|
||||
{% for item in group.entries %}
|
||||
<label class="chk-label">
|
||||
<input type="checkbox" data-pref-key="{{ item.key }}"{% if display.get(item.key, true) %} checked{% endif %}>
|
||||
<input type="checkbox" data-pref-key="{{ item.key }}"{% if item.key in ('show_nav_dashboard', 'show_nav_system_guide') %}{% if display.get(item.key) %} checked{% endif %}{% elif display.get(item.key, true) %} checked{% endif %}>
|
||||
{{ item.label }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
|
||||
@@ -314,6 +314,10 @@
|
||||
{% include 'risk_policy_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'system_guide' %}
|
||||
{% include 'system_guide_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'settings' %}
|
||||
{% include 'settings_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
{% if display.show_nav_risk_policy %}
|
||||
<a href="/risk_policy" data-embed-tab="risk_policy" class="{% if initial_tab == 'risk_policy' %}active{% endif %}">风控说明</a>
|
||||
{% endif %}
|
||||
<a href="/system_guide" data-embed-tab="system_guide" class="{% if initial_tab == 'system_guide' %}active{% endif %}"{% if not display.show_nav_system_guide %} style="display:none"{% endif %}>系统说明</a>
|
||||
{% if display.show_nav_env_config %}
|
||||
<a href="/env_config" data-embed-tab="env_config" class="{% if initial_tab == 'env_config' %}active{% endif %}">env配置</a>
|
||||
{% endif %}
|
||||
@@ -64,7 +65,7 @@
|
||||
<div id="embed-flash" class="flash" style="display:none" role="status"></div>
|
||||
|
||||
{% include 'instance_header_panel.html' %}
|
||||
{% if initial_tab not in ('settings', 'risk_policy', 'env_config') and include_transfer_block %}
|
||||
{% if initial_tab not in ('settings', 'risk_policy', 'system_guide', 'env_config') and include_transfer_block %}
|
||||
{% include 'instance_top_bar.html' %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
{% if display.show_nav_risk_policy %}
|
||||
<a href="/risk_policy" class="{% if page == 'risk_policy' %}active{% endif %}">风控说明</a>
|
||||
{% endif %}
|
||||
<a href="/system_guide" class="{% if page == 'system_guide' %}active{% endif %}"{% if not display.show_nav_system_guide %} style="display:none"{% endif %}>系统说明</a>
|
||||
{% if display.show_nav_env_config %}
|
||||
<a href="/env_config" class="{% if page == 'env_config' %}active{% endif %}">env配置</a>
|
||||
{% endif %}
|
||||
@@ -151,7 +152,7 @@
|
||||
{% with msg=get_flashed_messages() %}{% if msg %}<div class="flash">{{ msg[0] }}</div>{% endif %}{% endwith %}
|
||||
|
||||
{% include 'instance_header_panel.html' %}
|
||||
{% if page not in ('settings', 'risk_policy', 'env_config', 'options', 'options_review', 'hedge_plan') %}
|
||||
{% if page not in ('settings', 'risk_policy', 'system_guide', 'env_config', 'options', 'options_review', 'hedge_plan') %}
|
||||
{% include 'instance_top_bar.html' %}
|
||||
{% endif %}
|
||||
|
||||
@@ -390,6 +391,10 @@
|
||||
{% include 'risk_policy_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'system_guide' %}
|
||||
{% include 'system_guide_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page == 'settings' %}
|
||||
{% include 'settings_panel.html' %}
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
{# 系统说明: docs/系统说明.md + h2 目录 #}
|
||||
<div class="system-guide-page full">
|
||||
<div class="card system-guide-card">
|
||||
<div class="system-guide-head">
|
||||
<h2 style="margin:0">系统说明</h2>
|
||||
<p class="muted" style="margin:6px 0 0;font-size:.85rem">操作与逻辑按章节混排。默认不在顶栏显示;可在系统设置 → 导航显示中打开。</p>
|
||||
</div>
|
||||
<div class="system-guide-layout">
|
||||
{% if system_guide_toc %}
|
||||
<aside class="system-guide-toc" aria-label="章节目录">
|
||||
<div class="system-guide-toc-title">目录</div>
|
||||
<nav>
|
||||
{% for item in system_guide_toc %}
|
||||
<a href="#{{ item.id }}">{{ item.title }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
</aside>
|
||||
{% endif %}
|
||||
<article class="system-guide-body prose">
|
||||
{{ system_guide_html|safe }}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.system-guide-page { grid-column: 1 / -1; }
|
||||
.system-guide-card { padding: 16px 18px 28px; }
|
||||
.system-guide-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(160px, 220px) minmax(0, 1fr);
|
||||
gap: 18px;
|
||||
margin-top: 14px;
|
||||
align-items: start;
|
||||
}
|
||||
.system-guide-toc {
|
||||
position: sticky;
|
||||
top: 8px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(127,127,127,.25);
|
||||
border-radius: 8px;
|
||||
background: rgba(127,127,127,.06);
|
||||
max-height: calc(100vh - 120px);
|
||||
overflow: auto;
|
||||
}
|
||||
.system-guide-toc-title {
|
||||
font-size: .78rem;
|
||||
font-weight: 600;
|
||||
opacity: .75;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.system-guide-toc a {
|
||||
display: block;
|
||||
font-size: .84rem;
|
||||
line-height: 1.35;
|
||||
padding: 5px 0;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
opacity: .9;
|
||||
}
|
||||
.system-guide-toc a:hover { opacity: 1; text-decoration: underline; }
|
||||
.system-guide-body { min-width: 0; line-height: 1.65; font-size: .92rem; }
|
||||
.system-guide-body h1 { font-size: 1.35rem; margin: 0 0 12px; }
|
||||
.system-guide-body h2 { font-size: 1.12rem; margin: 22px 0 10px; padding-top: 4px; scroll-margin-top: 12px; }
|
||||
.system-guide-body h3 { font-size: 1rem; margin: 16px 0 8px; }
|
||||
.system-guide-body p, .system-guide-body li { margin: 0 0 8px; }
|
||||
.system-guide-body ul, .system-guide-body ol { padding-left: 1.35em; margin: 0 0 10px; }
|
||||
.system-guide-body table { border-collapse: collapse; width: 100%; margin: 10px 0 14px; font-size: .86rem; }
|
||||
.system-guide-body th, .system-guide-body td {
|
||||
border: 1px solid rgba(127,127,127,.35);
|
||||
padding: 7px 9px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.system-guide-body code {
|
||||
font-family: ui-monospace, Consolas, monospace;
|
||||
font-size: .86em;
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
background: rgba(127,127,127,.12);
|
||||
}
|
||||
.system-guide-body hr { border: 0; border-top: 1px solid rgba(127,127,127,.28); margin: 18px 0; }
|
||||
@media (max-width: 820px) {
|
||||
.system-guide-layout { grid-template-columns: 1fr; }
|
||||
.system-guide-toc { position: static; max-height: none; }
|
||||
.system-guide-toc nav { display: flex; flex-wrap: wrap; gap: 4px 12px; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user