mirror of
https://github.com/modelscope/FunASR.git
synced 2026-10-12 02:19:18 +00:00
docs: rebuild the learning, model and serving documentation system
Signed-off-by: zhifu gao <[email protected]>
This commit is contained in:
+104
-34
@@ -6,6 +6,7 @@ Output: gh-pages-output/api.html for deployment to gh-pages.
|
||||
This script is designed to be run by GitHub Actions on every push to main,
|
||||
automatically regenerating the API docs.
|
||||
"""
|
||||
import argparse
|
||||
import ast
|
||||
import glob
|
||||
import html as htmlmod
|
||||
@@ -17,7 +18,10 @@ from pathlib import Path
|
||||
REPO_URL = "https://github.com/modelscope/FunASR"
|
||||
BRANCH = "main"
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
OUTPUT_DIR = REPO_ROOT / "gh-pages-output"
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--output-dir", type=Path, default=REPO_ROOT / "gh-pages-output")
|
||||
OUTPUT_DIR = parser.parse_args().output_dir.resolve()
|
||||
STYLESHEET = REPO_ROOT / "gh-pages-output" / "api-reference.css"
|
||||
|
||||
os.chdir(REPO_ROOT)
|
||||
|
||||
@@ -164,22 +168,27 @@ print(f"Extracted: {len(all_data)} modules, {total_entries} entries")
|
||||
# Generate HTML
|
||||
sidebar = ""
|
||||
content = ""
|
||||
overview_links = ""
|
||||
eid = 0
|
||||
|
||||
for top_level in tree_structure:
|
||||
for group_index, top_level in enumerate(tree_structure):
|
||||
sub_modules = tree_structure[top_level]
|
||||
sidebar += f'<div class="l1"><div class="l1-title" onclick="toggleL1(this)"><span>{esc(top_level)}</span><span class="cnt">{sum(1 for s in sub_modules if f"{top_level}.{s}" in all_data)}</span><span class="arr">▸</span></div><div class="l1-children">\n'
|
||||
for sub_name in sub_modules:
|
||||
group_id = f"group-{group_index}"
|
||||
sidebar += f'<div class="l1"><button type="button" class="l1-title" aria-expanded="true" aria-controls="{group_id}"><span>{esc(top_level)}</span><span class="cnt">{sum(1 for s in sub_modules if f"{top_level}.{s}" in all_data)}</span><span class="arr" aria-hidden="true">▸</span></button><div class="l1-children" id="{group_id}">\n'
|
||||
for module_index, sub_name in enumerate(sub_modules):
|
||||
key = f"{top_level}.{sub_name}"
|
||||
if key not in all_data:
|
||||
continue
|
||||
sidebar += f'<div class="l2"><div class="l2-title" onclick="toggleL2(this)"><span>{esc(sub_name)}</span><span class="arr">▸</span></div><div class="l2-children">\n'
|
||||
module_id = f"{group_id}-module-{module_index}"
|
||||
sidebar += f'<div class="l2"><button type="button" class="l2-title" aria-expanded="true" aria-controls="{module_id}"><span>{esc(sub_name)}</span><span class="arr" aria-hidden="true">▸</span></button><div class="l2-children" id="{module_id}">\n'
|
||||
for entry in all_data[key]:
|
||||
eid += 1
|
||||
eid_str = f"e{eid}"
|
||||
filepath = entry.get("filepath", "")
|
||||
if entry["type"] == "class":
|
||||
sidebar += f'<a class="l3-item" data-target="{eid_str}" onclick="showEntry(\'{eid_str}\')"><span class="mb badge-c">C</span>{esc(entry["name"])}</a>\n'
|
||||
if entry["name"] in {"AutoModel", "RegisterTables"}:
|
||||
overview_links += f'<a href="#{eid_str}" class="entry-link">{esc(entry["name"])} <span aria-hidden="true">→</span></a> '
|
||||
sidebar += f'<a class="l3-item" href="#{eid_str}" data-target="{eid_str}"><span class="mb badge-c" aria-hidden="true">C</span><span>{esc(entry["name"])}</span></a>\n'
|
||||
gh_link = github_url(filepath, entry["lineno"])
|
||||
content += f'<div class="api-detail" id="{eid_str}"><span class="dtag badge-c">class</span><h2>{esc(entry["name"])}</h2><div class="dmod">{key} · <a href="{gh_link}" target="_blank">View on GitHub ↗</a></div><div class="ddoc">{format_doc(entry["doc"])}</div>'
|
||||
content += f'<details class="src-block"><summary>📄 Source code</summary><pre><code>{esc(entry["source"])}</code></pre>'
|
||||
@@ -200,7 +209,7 @@ for top_level in tree_structure:
|
||||
for m in entry["methods"]:
|
||||
eid += 1
|
||||
mid = f"e{eid}"
|
||||
sidebar += f'<a class="l3-item l3m" data-target="{mid}" onclick="showEntry(\'{mid}\')"><span class="mb badge-m">M</span>.{esc(m["name"])}()</a>\n'
|
||||
sidebar += f'<a class="l3-item l3m" href="#{mid}" data-target="{mid}"><span class="mb badge-m" aria-hidden="true">M</span><span>.{esc(m["name"])}()</span></a>\n'
|
||||
sig = f'{entry["name"]}.{m["name"]}({", ".join(m["args"])})'
|
||||
mgh = github_url(filepath, m["lineno"])
|
||||
content += f'<div class="api-detail" id="{mid}"><span class="dtag badge-m">method</span><h2><code>{esc(sig)}</code></h2><div class="dmod">{key}.{entry["name"]} · <a href="{mgh}" target="_blank">View on GitHub ↗</a></div><div class="ddoc">{format_doc(m["doc"])}</div>'
|
||||
@@ -211,7 +220,7 @@ for top_level in tree_structure:
|
||||
else:
|
||||
args_str = ", ".join(entry.get("args", []))
|
||||
gh_link = github_url(filepath, entry["lineno"])
|
||||
sidebar += f'<a class="l3-item" data-target="{eid_str}" onclick="showEntry(\'{eid_str}\')"><span class="mb badge-f">F</span>{esc(entry["name"])}</a>\n'
|
||||
sidebar += f'<a class="l3-item" href="#{eid_str}" data-target="{eid_str}"><span class="mb badge-f" aria-hidden="true">F</span><span>{esc(entry["name"])}</span></a>\n'
|
||||
content += f'<div class="api-detail" id="{eid_str}"><span class="dtag badge-f">function</span><h2><code>{esc(entry["name"])}({esc(args_str)})</code></h2><div class="dmod">{key} · <a href="{gh_link}" target="_blank">View on GitHub ↗</a></div><div class="ddoc">{format_doc(entry["doc"])}</div>'
|
||||
content += f'<details class="src-block"><summary>📄 Source code</summary><pre><code>{esc(entry["source"])}</code></pre>'
|
||||
if entry["truncated"]:
|
||||
@@ -220,53 +229,114 @@ for top_level in tree_structure:
|
||||
sidebar += '</div></div>\n'
|
||||
sidebar += '</div></div>\n'
|
||||
|
||||
# Write the full page (keeping same CSS structure + source styles)
|
||||
# Preserve extracted content and entry numbering while adding focus targets.
|
||||
content = content.replace('class="api-detail"', 'class="api-detail" tabindex="-1"')
|
||||
content = content.replace('target="_blank"', 'target="_blank" rel="noopener noreferrer"')
|
||||
content = content.replace('<summary>📄 Source', '<summary>Source')
|
||||
|
||||
html_page = f'''<!DOCTYPE html>
|
||||
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>FunASR API</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="api-reference.css">
|
||||
</head><body class="api-page">
|
||||
<nav class="nav"><div class="container">
|
||||
<a class="skip-link" href="#api-content">Skip to content</a>
|
||||
<nav class="nav" aria-label="Main navigation"><div class="container">
|
||||
<a href="index.html" class="nav-logo">FunASR</a>
|
||||
<div class="nav-links">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="tutorial.html">Tutorial</a>
|
||||
<a href="training.html">Training</a>
|
||||
<a href="model-registration.html">Develop</a>
|
||||
<a href="api.html" class="active">API</a>
|
||||
<a href="api.html" class="active" aria-current="page">API</a>
|
||||
</div>
|
||||
<a href="https://github.com/modelscope/FunASR" class="nav-github">GitHub</a>
|
||||
</div></nav>
|
||||
<header class="api-header"><h1>API Reference</h1><p>{len(all_data)} modules <span aria-hidden="true">/</span> {total_entries} classes and functions</p></header>
|
||||
<div class="api-layout">
|
||||
<div class="api-sidebar">
|
||||
<input type="text" class="sb-search" placeholder="Search..." oninput="filterSidebar(this.value)">
|
||||
<aside class="api-sidebar" aria-label="API navigation">
|
||||
<label class="sr-only" for="api-search">Search API entries</label>
|
||||
<input id="api-search" type="search" class="sb-search" placeholder="Search API" autocomplete="off" spellcheck="false">
|
||||
<p id="search-empty" class="search-empty" role="status" hidden>No matching APIs</p>
|
||||
<nav aria-label="Modules and entries">
|
||||
{sidebar}
|
||||
</div>
|
||||
<div class="api-content">
|
||||
<div class="api-welcome" id="api-welcome"><h2>API Reference</h2><p>{total_entries} entries · auto-generated from source<br>Click source code to expand. Links point to latest GitHub code.</p></div>
|
||||
</nav></aside>
|
||||
<main class="api-content" id="api-content" tabindex="-1">
|
||||
<div class="api-welcome" id="api-welcome"><h2>FunASR Python API</h2><p class="muted">Models, inference, training, and utilities.</p>{overview_links}</div>
|
||||
{content}
|
||||
</div></div>
|
||||
</main></div>
|
||||
<script>
|
||||
function toggleL1(el){{el.closest('.l1').classList.toggle('open')}}
|
||||
function toggleL2(el){{el.closest('.l2').classList.toggle('open')}}
|
||||
function showEntry(id){{
|
||||
document.querySelectorAll('.api-detail').forEach(e=>e.classList.remove('active'));
|
||||
document.querySelectorAll('.l3-item').forEach(e=>e.classList.remove('active'));
|
||||
document.getElementById('api-welcome').style.display='none';
|
||||
var t=document.getElementById(id);if(t)t.classList.add('active');
|
||||
var l=document.querySelector('[data-target="'+id+'"]');if(l)l.classList.add('active');
|
||||
document.querySelector('.api-content').scrollTop=0;
|
||||
const entries = Array.from(document.querySelectorAll('.api-detail'));
|
||||
const entryLinks = Array.from(document.querySelectorAll('.l3-item'));
|
||||
const controls = Array.from(document.querySelectorAll('.l1-title, .l2-title'));
|
||||
const welcome = document.getElementById('api-welcome');
|
||||
const search = document.getElementById('api-search');
|
||||
let expandedBeforeSearch = null;
|
||||
|
||||
function setExpanded(button, expanded) {{
|
||||
button.setAttribute('aria-expanded', String(expanded));
|
||||
document.getElementById(button.getAttribute('aria-controls')).hidden = !expanded;
|
||||
}}
|
||||
function filterSidebar(q){{
|
||||
q=q.toLowerCase();
|
||||
document.querySelectorAll('.l3-item').forEach(e=>e.classList.toggle('hidden',q&&!e.textContent.toLowerCase().includes(q)));
|
||||
if(q)document.querySelectorAll('.l1,.l2').forEach(e=>e.classList.add('open'));
|
||||
function showEntry(id, focus = false) {{
|
||||
// Resolve only known entries, never interpolate an untrusted hash in a selector.
|
||||
const target = entries.find(entry => entry.id === id) || null;
|
||||
entries.forEach(entry => {{ entry.hidden = entry !== target; }});
|
||||
welcome.hidden = Boolean(target);
|
||||
entryLinks.forEach(link => {{
|
||||
if (target && link.dataset.target === target.id) {{
|
||||
link.setAttribute('aria-current', 'true');
|
||||
if (link.hidden) {{ search.value = ''; filterSidebar(''); }}
|
||||
for (const level of ['.l1', '.l2']) {{
|
||||
const group = link.closest(level);
|
||||
if (group) setExpanded(group.querySelector('button'), true);
|
||||
}}
|
||||
}} else {{
|
||||
link.removeAttribute('aria-current');
|
||||
}}
|
||||
}});
|
||||
document.title = target ? target.querySelector('h2').textContent + ' | FunASR API' : 'FunASR API';
|
||||
if (focus && target) {{
|
||||
target.focus({{preventScroll: true}});
|
||||
target.scrollIntoView({{block: 'start'}});
|
||||
}}
|
||||
}}
|
||||
if(window.location.hash)showEntry(window.location.hash.slice(1));
|
||||
function filterSidebar(query) {{
|
||||
const q = query.trim().toLowerCase();
|
||||
if (q && !expandedBeforeSearch) {{
|
||||
expandedBeforeSearch = new Map(controls.map(button => [button, button.getAttribute('aria-expanded') === 'true']));
|
||||
}}
|
||||
entryLinks.forEach(link => {{ link.hidden = Boolean(q) && !link.textContent.toLowerCase().includes(q); }});
|
||||
document.querySelectorAll('.l2, .l1').forEach(group => {{
|
||||
group.hidden = Boolean(q) && !Array.from(group.querySelectorAll('.l3-item')).some(link => !link.hidden);
|
||||
}});
|
||||
if (q) controls.forEach(button => setExpanded(button, true));
|
||||
else if (expandedBeforeSearch) {{
|
||||
expandedBeforeSearch.forEach((expanded, button) => setExpanded(button, expanded));
|
||||
expandedBeforeSearch = null;
|
||||
}}
|
||||
document.getElementById('search-empty').hidden = !q || entryLinks.some(link => !link.hidden);
|
||||
}}
|
||||
controls.forEach(button => {{
|
||||
setExpanded(button, false);
|
||||
button.addEventListener('click', () => setExpanded(button, button.getAttribute('aria-expanded') !== 'true'));
|
||||
}});
|
||||
entryLinks.forEach(link => link.addEventListener('click', event => {{
|
||||
if (!event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey && event.button === 0) {{
|
||||
showEntry(link.dataset.target, true);
|
||||
}}
|
||||
}}));
|
||||
search.addEventListener('input', () => filterSidebar(search.value));
|
||||
document.querySelector('.skip-link').addEventListener('click', event => {{
|
||||
if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey || event.button !== 0) return;
|
||||
event.preventDefault();
|
||||
document.getElementById('api-content').focus();
|
||||
}});
|
||||
window.addEventListener('hashchange', () => showEntry(window.location.hash.slice(1)));
|
||||
showEntry(window.location.hash.slice(1));
|
||||
</script></body></html>'''
|
||||
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
if STYLESHEET.resolve() != (OUTPUT_DIR / STYLESHEET.name).resolve():
|
||||
shutil.copy2(STYLESHEET, OUTPUT_DIR / STYLESHEET.name)
|
||||
with (OUTPUT_DIR / "api.html").open("w", encoding="utf-8") as f:
|
||||
f.write(html_page)
|
||||
|
||||
@@ -274,4 +344,4 @@ training_src = REPO_ROOT / "training.html"
|
||||
if training_src.exists():
|
||||
shutil.copy2(training_src, OUTPUT_DIR / "training.html")
|
||||
|
||||
print("Generated gh-pages-output/api.html")
|
||||
print(f"Generated {OUTPUT_DIR / 'api.html'}")
|
||||
|
||||
Reference in New Issue
Block a user