diff --git a/src/handler/doc.rs b/src/handler/doc.rs index 5c0ae93..4f7c44e 100644 --- a/src/handler/doc.rs +++ b/src/handler/doc.rs @@ -21,3 +21,20 @@ pub async fn get_api_md() -> Result { Ok((StatusCode::OK, headers, content)) } + +pub async fn get_readme_md() -> Result { + let content = tokio::fs::read_to_string("README.md") + .await + .map_err(|err| { + tracing::error!("Failed to read README.md: {}", err); + ApiErr::NotFound("README.md not found".to_string(), None) + })?; + + let mut headers = HeaderMap::new(); + headers.insert( + header::CONTENT_TYPE, + HeaderValue::from_static("text/markdown; charset=utf-8"), + ); + + Ok((StatusCode::OK, headers, content)) +} diff --git a/src/main.rs b/src/main.rs index c857203..54a4745 100644 --- a/src/main.rs +++ b/src/main.rs @@ -280,7 +280,8 @@ fn build_router(state: AppState) -> Router { ) .route("/api/logs", get(handler::log::get_logs)) .route("/api/logs/stream", get(handler::log::stream_logs)) - .route("/api/docs/api-md", get(handler::doc::get_api_md)); + .route("/api/docs/api-md", get(handler::doc::get_api_md)) + .route("/api/docs/readme-md", get(handler::doc::get_readme_md)); Router::new() .merge(all_route) diff --git a/web/html/topbar.html b/web/html/topbar.html index bfcf09d..5169ac8 100644 --- a/web/html/topbar.html +++ b/web/html/topbar.html @@ -5,6 +5,7 @@
+
diff --git a/web/js/app.js b/web/js/app.js index 552c249..c7e36a0 100644 --- a/web/js/app.js +++ b/web/js/app.js @@ -1,7 +1,7 @@ import { withStatus } from "./api.js"; import { openChart, renderChart } from "./chart.js"; import { dom } from "./dom.js"; -import { closeApiDocDrawer, openApiDocDrawer } from "./docs.js"; +import { closeApiDocDrawer, openApiDocDrawer, openReadmeDrawer } from "./docs.js"; import { loadEvents } from "./events.js"; import { applyBatchEquipmentUnit, @@ -130,6 +130,7 @@ function bindEvents() { }); }); + dom.openReadmeDocBtn.addEventListener("click", () => withStatus(openReadmeDrawer())); dom.openApiDocBtn.addEventListener("click", () => withStatus(openApiDocDrawer())); dom.closeApiDocBtn.addEventListener("click", closeApiDocDrawer); dom.refreshEventBtn.addEventListener("click", () => withStatus(loadEvents())); diff --git a/web/js/docs.js b/web/js/docs.js index f8e08e6..a0a4a80 100644 --- a/web/js/docs.js +++ b/web/js/docs.js @@ -82,11 +82,11 @@ function parseMarkdown(text) { return { html: blocks.join(""), headings }; } -export async function loadApiDoc() { - const text = await apiFetch("/api/docs/api-md"); +async function loadDoc(url, emptyMessage) { + const text = await apiFetch(url); const { html, headings } = parseMarkdown(text || ""); - dom.apiDocContent.innerHTML = html || "

API.md 为空

"; + dom.apiDocContent.innerHTML = html || `

${emptyMessage}

`; dom.apiDocToc.innerHTML = headings.length ? headings .map( @@ -110,14 +110,25 @@ export async function loadApiDoc() { } }); }); - - state.apiDocLoaded = true; } export async function openApiDocDrawer() { + const title = dom.apiDocDrawer.querySelector("h3"); + if (title) title.textContent = "API.md"; dom.apiDocDrawer.classList.remove("hidden"); - if (!state.apiDocLoaded) { - await loadApiDoc(); + if (state.docDrawerSource !== "api") { + state.docDrawerSource = "api"; + await loadDoc("/api/docs/api-md", "API.md 为空"); + } +} + +export async function openReadmeDrawer() { + const title = dom.apiDocDrawer.querySelector("h3"); + if (title) title.textContent = "README.md"; + dom.apiDocDrawer.classList.remove("hidden"); + if (state.docDrawerSource !== "readme") { + state.docDrawerSource = "readme"; + await loadDoc("/api/docs/readme-md", "README.md 为空"); } } diff --git a/web/js/dom.js b/web/js/dom.js index 3eab7d7..4636cf4 100644 --- a/web/js/dom.js +++ b/web/js/dom.js @@ -81,6 +81,7 @@ export const dom = { batchBindingSignalRole: byId("batchBindingSignalRole"), apiDocToc: byId("apiDocToc"), apiDocContent: byId("apiDocContent"), + openReadmeDocBtn: byId("openReadmeDoc"), openApiDocBtn: byId("openApiDoc"), closeApiDocBtn: byId("closeApiDoc"), refreshChartBtn: byId("refreshChart"), diff --git a/web/js/state.js b/web/js/state.js index 7cbf656..4c70a2a 100644 --- a/web/js/state.js +++ b/web/js/state.js @@ -19,7 +19,7 @@ export const state = { chartPointName: "", chartData: [], pointSocket: null, - apiDocLoaded: false, + docDrawerSource: null, // null | "api" | "readme" runtimes: new Map(), // unit_id -> UnitRuntime activeView: "ops", // "ops" | "config" opsPointEls: new Map(), // point_id -> { pillEl, syncBtns? }