"""
将 v2/main.md 转成 main.html。
特点：
- 嵌套目录（可折叠/展开）
- 主题切换（浅色/深色/护眼）
- 字体缩放
- 响应式布局
- 百度统计
- 锚点跳转
"""
import html
import re
from pathlib import Path


ROOT = Path(r"E:\GitHub3\article\艺术理论\v2")
SOURCE_MD = ROOT / "main.md"
TARGET_HTML = ROOT / "main.html"


def load_markdown(file_path: Path) -> list[str]:
    """读取 Markdown 文件并返回逐行文本。"""
    return file_path.read_text(encoding="utf-8").splitlines()


def escape_html(text: str) -> str:
    """HTML 转义。"""
    return html.escape(text, quote=True)


def format_inline(text: str) -> str:
    """处理行内链接、代码和加粗，避免正文中的特殊字符破坏 HTML。"""
    # 先用占位符保护代码块和图片
    placeholders = {}

    def protect(pattern, key_prefix, text):
        def repl(m):
            key = f"@@{key_prefix}_{len(placeholders)}@@"
            placeholders[key] = m.group(0)
            return key
        return re.sub(pattern, repl, text)

    # 必须先保护图片（包含 ! 前缀），再保护链接（避免 LINK 误吞 IMG 的 [...]）
    # 保护图片
    text = protect(r"!\[([^\]]*)\]\(([^)]+)\)", "IMG", text)
    # 保护链接
    text = protect(r"\[([^\]]+)\]\(([^)]+)\)", "LINK", text)
    # 保护行内代码
    text = protect(r"`([^`]+)`", "CODE", text)

    # 转义剩余 HTML 字符
    text = escape_html(text)

    # 处理加粗（**xxx**）
    text = re.sub(r"\*\*([^*]+)\*\*", r"<strong>\1</strong>", text)
    # 处理斜体（*xxx* 或 _xxx_）
    text = re.sub(r"(?<!\*)\*([^*\n]+)\*(?!\*)", r"<em>\1</em>", text)

    # 还原占位符
    def restore(m):
        key = m.group(0)
        original = placeholders.get(key, key)
        if original.startswith("[") and "](" in original:
            # 还原链接
            link_match = re.match(r"\[([^\]]+)\]\(([^)]+)\)", original)
            if link_match:
                return f'<a href="{escape_html(link_match.group(2))}">{escape_html(link_match.group(1))}</a>'
        elif original.startswith("`"):
            # 还原代码
            code_match = re.match(r"`([^`]+)`", original)
            if code_match:
                return f"<code>{escape_html(code_match.group(1))}</code>"
        elif original.startswith("!["):
            # 还原图片
            img_match = re.match(r"!\[([^\]]*)\]\(([^)]+)\)", original)
            if img_match:
                alt = escape_html(img_match.group(1))
                src = escape_html(img_match.group(2))
                # SVG 自动用 figure 包装
                if src.lower().endswith(".svg"):
                    return f'<figure class="article-svg-figure"><img src="{src}" alt="{alt}" loading="lazy">{f"<figcaption>{alt}</figcaption>" if alt else ""}</figure>'
                return f'<img src="{src}" alt="{alt}">'
        return escape_html(original)

    text = re.sub(r"@@[A-Z]+_\d+@@", restore, text)
    return text


def heading_id(index: int) -> str:
    """生成稳定的标题锚点，便于页面内跳转。"""
    return f"heading-{index}"


def is_table_separator(line: str) -> bool:
    """判断一行是否为 Markdown 表格的分隔线。"""
    compact = line.replace(" ", "")
    return bool(re.fullmatch(r"\|?[:\-|]+\|?", compact)) and "-" in compact


def parse_table(lines: list[str], start: int) -> tuple[str, int]:
    """把连续的 Markdown 表格转换为 HTML。"""
    header_line = lines[start].strip()
    if start + 1 >= len(lines):
        return "", start
    separator_line = lines[start + 1].strip()
    if "|" not in header_line or not is_table_separator(separator_line):
        return "", start

    rows: list[list[str]] = []
    index = start
    while index < len(lines):
        line = lines[index].strip()
        if "|" not in line or not line:
            break
        cells = [format_inline(cell.strip()) for cell in line.strip("|").split("|")]
        rows.append(cells)
        index += 1

    if len(rows) < 2:
        return "", start

    header_cells = "".join(f"<th>{cell}</th>" for cell in rows[0])
    body_rows = []
    for row in rows[2:]:
        body_rows.append("<tr>" + "".join(f"<td>{cell}</td>" for cell in row) + "</tr>")

    table_html = (
        "<div class=\"table-wrap\"><table>"
        f"<thead><tr>{header_cells}</tr></thead>"
        f"<tbody>{''.join(body_rows)}</tbody>"
        "</table></div>"
    )
    return table_html, index - 1


def build_article(lines: list[str]) -> tuple[str, list[dict], str]:
    """把 Markdown 正文转换为 HTML，并抽取页面标题和提纲。"""
    blocks: list[str] = []
    outline: list[dict] = []
    title = "艺术理论 v2"
    heading_count = 0
    in_ul = False
    in_ol = False
    blockquote_lines: list[str] = []

    def close_lists() -> None:
        nonlocal in_ul, in_ol
        if in_ul:
            blocks.append("</ul>")
            in_ul = False
        if in_ol:
            blocks.append("</ol>")
            in_ol = False

    def close_blockquote() -> None:
        nonlocal blockquote_lines
        if blockquote_lines:
            blocks.append("<blockquote>" + "<br>".join(blockquote_lines) + "</blockquote>")
            blockquote_lines = []

    index = 0
    while index < len(lines):
        raw = lines[index]
        line = raw.strip()

        if not line:
            close_lists()
            close_blockquote()
            index += 1
            continue

        if line.startswith("> "):
            close_lists()
            blockquote_lines.append(format_inline(line[2:].strip()))
            index += 1
            continue

        close_blockquote()

        # 表格
        if index + 1 < len(lines):
            table_html, table_end = parse_table(lines, index)
            if table_html:
                close_lists()
                blocks.append(table_html)
                index = table_end + 1
                continue

        # 标题
        if line.startswith("# "):
            close_lists()
            heading_count += 1
            anchor = heading_id(heading_count)
            heading = line[2:].strip()
            title = heading
            blocks.append(f"<h1 id=\"{anchor}\">{format_inline(heading)}</h1>")
            outline.append({"level": 1, "title": heading, "id": anchor})
            index += 1
            continue

        if line.startswith("## "):
            close_lists()
            heading_count += 1
            anchor = heading_id(heading_count)
            heading = line[3:].strip()
            blocks.append(f"<h2 id=\"{anchor}\">{format_inline(heading)}</h2>")
            outline.append({"level": 2, "title": heading, "id": anchor})
            index += 1
            continue

        if line.startswith("### "):
            close_lists()
            heading_count += 1
            anchor = heading_id(heading_count)
            heading = line[4:].strip()
            blocks.append(f"<h3 id=\"{anchor}\">{format_inline(heading)}</h3>")
            outline.append({"level": 3, "title": heading, "id": anchor})
            index += 1
            continue

        if line.startswith("#### "):
            close_lists()
            heading_count += 1
            anchor = heading_id(heading_count)
            heading = line[5:].strip()
            blocks.append(f"<h4 id=\"{anchor}\">{format_inline(heading)}</h4>")
            outline.append({"level": 4, "title": heading, "id": anchor})
            index += 1
            continue

        # 任务列表或无序列表
        if line.startswith("- ") or line.startswith("* "):
            if not in_ul:
                close_lists()
                blocks.append("<ul>")
                in_ul = True
            item = line[2:].strip()
            # 检查是否是任务列表
            if item.startswith("[ ] "):
                item = item[4:]
                blocks.append(f"<li class=\"task-item task-pending\">☐ {format_inline(item)}</li>")
            elif item.startswith("[x] ") or item.startswith("[X] "):
                item = item[4:]
                blocks.append(f"<li class=\"task-item task-done\">☑ {format_inline(item)}</li>")
            else:
                blocks.append(f"<li>{format_inline(item)}</li>")
            index += 1
            continue

        if re.match(r"^\d+\.\s", line):
            if not in_ol:
                close_lists()
                blocks.append("<ol>")
                in_ol = True
            item = re.sub(r"^\d+\.\s*", "", line)
            blocks.append(f"<li>{format_inline(item)}</li>")
            index += 1
            continue

        # 分隔线
        if line == "---":
            close_lists()
            blocks.append("<hr>")
            index += 1
            continue

        close_lists()
        # 独立成块的元素：figure（SVG 图片）不应被 <p> 包裹
        inline_html = format_inline(line)
        if inline_html.lstrip().startswith("<figure"):
            blocks.append(inline_html)
        else:
            blocks.append(f"<p>{inline_html}</p>")
        index += 1

    close_lists()
    close_blockquote()
    return "\n".join(blocks), outline, title


def build_toc_html(outline: list[dict]) -> str:
    """生成嵌套的目录 HTML。"""
    if not outline:
        return ""

    # 标记每个节点是否有子节点
    for i, entry in enumerate(outline):
        entry["has_children"] = False
        for ne in outline[i + 1:]:
            if ne["level"] <= entry["level"]:
                break
            if ne["level"] > entry["level"]:
                entry["has_children"] = True
                break

    parts = ['<ul class="toc-tree">']
    open_stack = []  # [level, ...]

    def close_one():
        open_stack.pop()
        parts.append('</ul></div></li>')

    def close_to_level(target_level):
        while open_stack and open_stack[-1] > target_level:
            close_one()

    for entry in outline:
        level = entry["level"]
        title = entry["title"]
        anchor = entry["id"]
        has_children = entry["has_children"]
        # 关闭到 level - 1
        close_to_level(level - 1)
        css_class = f"toc-h{level}"
        if has_children:
            parts.append(
                f'<li class="{css_class} toc-branch">'
                f'<div class="toc-item-row">'
                f'<button type="button" class="toc-toggle" '
                f'data-toc-toggle="toc-node-{anchor}" '
                f'aria-expanded="true">−</button>'
                f'<a href="#{anchor}">{escape_html(title)}</a>'
                f'</div>'
                f'<div class="toc-children" id="toc-node-{anchor}">'
                f'<ul class="toc-tree">'
            )
            open_stack.append(level)
        else:
            parts.append(
                f'<li class="{css_class}">'
                f'<div class="toc-item-row">'
                f'<span class="toc-spacer" aria-hidden="true"></span>'
                f'<a href="#{anchor}">{escape_html(title)}</a>'
                f'</div></li>'
            )

    while open_stack:
        close_one()
    parts.append('</ul>')
    return "".join(parts)


def build_html(article_html: str, outline: list[dict], title: str) -> str:
    """组装完整的 HTML 页面。"""
    toc_html = build_toc_html(outline)

    # 统计各级标题
    h1_count = sum(1 for o in outline if o["level"] == 1)
    h2_count = sum(1 for o in outline if o["level"] == 2)
    h3_count = sum(1 for o in outline if o["level"] == 3)
    h4_count = sum(1 for o in outline if o["level"] == 4)

    return f"""<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{escape_html(title)} · 艺术理论 v2</title>
<link rel="stylesheet" href="css/entry-common.css">
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{
  font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
  line-height: 1.85;
  color: var(--text);
  background: var(--bg);
  min-height: 100vh;
  font-size: calc(16px * var(--font-scale, 1));
}}
.layout {{
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 24px;
  max-width: 1280px;
  margin: 0 auto;
  padding: 24px 20px;
}}
aside.toc {{
  position: sticky;
  top: 20px;
  align-self: start;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  padding: 18px 20px;
  font-size: 0.92em;
}}
aside.toc .toc-title {{
  font-size: 0.95em;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 2px solid var(--link);
}}
.toc-tree {{
  list-style: none;
  padding-left: 0;
}}
.toc-tree ul {{
  list-style: none;
  padding-left: 18px;
  margin: 4px 0;
}}
.toc-item-row {{
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 0;
  line-height: 1.5;
}}
.toc-toggle {{
  background: none;
  border: none;
  color: var(--link);
  cursor: pointer;
  font-size: 1em;
  font-weight: 700;
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex-shrink: 0;
}}
.toc-toggle:hover {{
  color: var(--link-hover);
}}
.toc-spacer {{
  display: inline-block;
  width: 18px;
  flex-shrink: 0;
}}
.toc-tree a {{
  color: var(--text);
  text-decoration: none;
  flex: 1;
}}
.toc-tree a:hover {{
  color: var(--link);
  text-decoration: underline;
}}
.toc-h1 a {{ font-weight: 700; font-size: 1.0em; }}
.toc-h2 a {{ font-weight: 600; }}
.toc-h3 a {{ color: var(--muted); font-size: 0.92em; }}
.toc-h4 a {{ color: var(--muted); font-size: 0.88em; padding-left: 4px; }}

main.article {{
  background: var(--panel-bg);
  padding: 36px 44px;
  border-radius: 12px;
  border: 1px solid var(--panel-border);
  line-height: 1.9;
  font-size: 1.0em;
  /* 右侧留出空间给"阅读设置"浮动面板（约 240px），避免遮挡 */
  /* 使用 !important 覆盖 entry-common.css 中同 main 选择器的 @media 规则 */
  padding-right: 240px !important;
  max-width: 100%;
}}
/* 仅在很窄的屏幕（移动端）下取消右侧留白 */
@media (max-width: 700px) {{
  main.article {{ padding-right: 24px !important; }}
}}
.article-header {{
  background: linear-gradient(135deg, #7c3aed 0%, #5b21b6 100%);
  color: #ffffff;
  padding: 40px 32px;
  border-radius: 12px;
  margin-bottom: 30px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.12);
}}
.article-header h1 {{
  font-size: 1.9em;
  font-weight: 700;
  margin-bottom: 12px;
  letter-spacing: 0.5px;
  border: none;
  padding: 0;
  color: #ffffff;
}}
.article-header .meta {{
  font-size: 0.92em;
  opacity: 0.92;
  line-height: 1.8;
}}
.article-header .meta a {{
  color: #ddd6fe;
  text-decoration: none;
}}
.article-header .meta a:hover {{
  text-decoration: underline;
}}
.article h1 {{
  display: none; /* 顶部已展示 */
}}
.article h2 {{
  font-size: 1.55em;
  font-weight: 700;
  color: var(--text);
  border-left: 5px solid #7c3aed;
  padding-left: 14px;
  margin: 2.0em 0 0.8em 0;
  line-height: 1.4;
}}
.article h3 {{
  font-size: 1.22em;
  font-weight: 700;
  color: var(--text);
  margin: 1.6em 0 0.6em 0;
  border-bottom: 1px dashed var(--panel-border);
  padding-bottom: 6px;
}}
.article h4 {{
  font-size: 1.05em;
  font-weight: 600;
  color: var(--muted);
  margin: 1.2em 0 0.5em 0;
}}
.article p {{
  margin: 0.9em 0;
  color: var(--text);
}}
.article strong {{
  color: #7c3aed;
  font-weight: 700;
}}
.article em {{
  color: var(--link);
  font-style: italic;
}}
.article a {{
  color: var(--link);
  text-decoration: none;
  border-bottom: 1px solid var(--link);
}}
.article a:hover {{
  color: var(--link-hover);
  border-bottom-color: var(--link-hover);
}}
.article ul, .article ol {{
  margin: 0.8em 0 0.8em 1.8em;
  color: var(--text);
}}
.article li {{
  margin: 0.4em 0;
  line-height: 1.8;
}}
.article li.task-pending {{
  list-style: none;
  margin-left: -1.4em;
}}
.article li.task-done {{
  list-style: none;
  margin-left: -1.4em;
  color: var(--muted);
  text-decoration: line-through;
}}
.article blockquote {{
  border-left: 4px solid #7c3aed;
  background: var(--quote-bg);
  padding: 14px 20px;
  margin: 1.2em 0;
  border-radius: 0 8px 8px 0;
  color: var(--text);
}}
.article hr {{
  border: none;
  border-top: 2px dashed var(--panel-border);
  margin: 2.5em 0;
}}
.article code {{
  background: var(--pill-bg);
  padding: 2px 6px;
  border-radius: 4px;
  font-family: "Consolas", "Monaco", monospace;
  font-size: 0.9em;
  color: #7c3aed;
}}
.article table {{
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  margin: 1.4em 0;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 4px 18px rgba(31, 41, 55, 0.08);
  border: 1px solid var(--panel-border);
  font-size: 0.94em;
}}
.article thead {{
  background: linear-gradient(135deg, #7c3aed 0%, #5b21b6 100%);
  color: #ffffff;
}}
.article th, .article td {{
  border: none;
  border-bottom: 1px solid var(--panel-border);
  border-right: 1px solid var(--panel-border);
  padding: 11px 14px;
  text-align: left;
  line-height: 1.6;
  color: var(--text);
}}
.article th:last-child, .article td:last-child {{
  border-right: none;
}}
.article tbody tr:last-child td {{
  border-bottom: none;
}}
.article th {{
  font-weight: 700;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
  color: #ffffff;
}}
.article tbody tr {{
  background: var(--panel-bg);
  transition: background-color 0.2s ease;
}}
.article tbody tr:nth-child(even) {{
  background: var(--bg);
}}
.article tbody tr:hover {{
  background: var(--quote-bg);
}}
.table-wrap {{
  overflow-x: auto;
  margin: 1.2em 0;
}}

.toc-stat {{
  display: flex;
  gap: 8px;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--panel-border);
  font-size: 0.78em;
  color: var(--muted);
}}
.toc-stat span {{
  padding: 2px 8px;
  background: var(--pill-bg);
  border-radius: 4px;
}}

.nav-footer {{
  margin-top: 40px;
  padding: 20px 24px;
  background: var(--quote-bg);
  border-left: 4px solid #7c3aed;
  border-radius: 8px;
  font-size: 0.92em;
}}
.nav-footer a {{
  color: var(--link);
  text-decoration: none;
  margin-right: 14px;
}}
.nav-footer a:hover {{
  text-decoration: underline;
}}

@media (max-width: 900px) {{
  .layout {{
    grid-template-columns: 1fr;
  }}
  aside.toc {{
    position: static;
    max-height: none;
  }}
  main.article {{
    padding: 24px 22px;
  }}
  .article h1 {{ font-size: 1.4em; }}
  .article h2 {{ font-size: 1.25em; }}
  .article table {{ font-size: 0.88em; display: block; overflow-x: auto; white-space: nowrap; }}
}}

/* 文章内 SVG 插图样式：浅黄渐变背景、圆角、阴影、hover 缩放 */
.article-svg-figure {{
  margin: 2em auto;
  padding: 0;
  text-align: center;
  max-width: 100%;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 6px 22px rgba(15, 23, 42, 0.10);
  background: transparent;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}}
.article-svg-figure:hover {{
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.15);
}}
.article-svg-figure img {{
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
  margin: 0 auto;
}}
.article-svg-figure figcaption {{
  padding: 10px 16px 12px;
  font-size: 0.88em;
  color: var(--muted);
  background: var(--quote-bg);
  border-top: 1px solid var(--panel-border);
  font-style: italic;
}}
</style>
</head>
<body>
<aside class="font-zoom is-collapsed" id="font-zoom-panel" aria-label="阅读设置">
  <button type="button" class="font-zoom-toggle" data-font-zoom-toggle aria-label="打开阅读设置">
    <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <text x="12" y="16" text-anchor="middle" font-size="14" font-weight="700" fill="currentColor" stroke="none">Aa</text>
      <line x1="3" y1="20" x2="21" y2="20"/>
    </svg>
  </button>
  <div class="font-zoom-body">
    <div class="font-zoom-label">阅读设置</div>
    <button type="button" data-font-zoom="decrease">A-</button>
    <button type="button" data-font-zoom="reset">重置</button>
    <button type="button" data-font-zoom="increase">A+</button>
    <select data-theme-mode="select" aria-label="主题">
      <option value="auto">主题：自动</option>
      <option value="light">主题：浅色</option>
      <option value="dark">主题：深色</option>
      <option value="black">主题：黑色护眼</option>
      <option value="forest">主题：暗绿护眼</option>
      <option value="slate">主题：暗灰蓝护眼</option>
      <option value="purple">主题：暗紫护眼</option>
      <option value="sepia">主题：暖色护眼</option>
    </select>
  </div>
</aside>
<div class="layout">
  <aside class="toc" aria-label="本节目录">
    <div class="toc-title">本节目录</div>
    {toc_html}
    <div class="toc-stat">
      <span>H1 × {h1_count}</span>
      <span>H2 × {h2_count}</span>
      <span>H3 × {h3_count}</span>
      <span>H4 × {h4_count}</span>
    </div>
  </aside>
  <main class="article">
    <div class="article-header">
      <h1>{escape_html(title)}</h1>
      <div class="meta">
        <p>版本：v2.0（2026-07-19）· 由 v1.22 完整复制 + 整合「附录 T:武器与地图」</p>
        <p>母理论：<a href="../../四把尺子理论/v26/main.html">四把尺子理论 v26</a></p>
        <p>关键源材料：<a href="../../四把尺子理论/v26/new/9.html">new/9.md - 短剧的意义：未被制度承认的温暖</a> · <a href="new/1.html">new/1.md - 附录 T:武器与地图</a></p>
        <p>一句话：v2 在 v1「温暖」引擎的基础上,加入「武器与地图」第二引擎——给创作者一套既能"建造庇护所"又能"锻造武器和地图"的完整框架:庇护所让你有力气走出去,武器和地图让你在外面走得更远。</p>
      </div>
    </div>
    {article_html}
    <div class="nav-footer">
      <strong style="color:var(--text)">延伸阅读 ·</strong>
      <a href="../../四把尺子理论/v26/main.html">母理论 v26</a>
      <a href="../../四把尺子理论/v26/new/9.html">new/9 短剧的意义</a>
      <a href="new/1.html">new/22 附录 T</a>
      <a href="../健康理论/v1/main.html">健康理论 v1</a>
      <a href="index.html">艺术理论 v2 入口</a>
      <a href="README.md">README</a>
    </div>
  </main>
</div>
<script src="js/article.js"></script>
<script>
// 目录折叠展开
document.addEventListener('click', function(e) {{
  var btn = e.target.closest('[data-toc-toggle]');
  if (!btn) return;
  var node = document.getElementById(btn.getAttribute('data-toc-toggle'));
  if (!node) return;
  var expanded = btn.getAttribute('aria-expanded') === 'true';
  if (expanded) {{
    node.style.display = 'none';
    btn.setAttribute('aria-expanded', 'false');
    btn.textContent = '+';
  }} else {{
    node.style.display = '';
    btn.setAttribute('aria-expanded', 'true');
    btn.textContent = '−';
  }}
}});
</script>
<script type="text/javascript" src="https://tongji.funnyai.com/js/baidu_tongji.js"></script>
</body>
</html>
"""


def main() -> None:
    """主流程：读 MD → 转 HTML → 写文件。"""
    lines = load_markdown(SOURCE_MD)
    article_html, outline, title = build_article(lines)
    final_html = build_html(article_html, outline, title)
    TARGET_HTML.write_text(final_html, encoding="utf-8")
    print(f"[OK] 生成 {TARGET_HTML}（{len(final_html):,} 字符，{len(outline)} 个标题）")


if __name__ == "__main__":
    main()
