use axum::http::header::CONTENT_TYPE;
use axum::response::IntoResponse;
const OPENAPI_JSON: &str = include_str!("openapi.json");
const SYNBIOHUB_OPENAPI_JSON: &str = include_str!("synbiohub_openapi.json");
const SCALAR_THEME_CSS: &str = include_str!("scalar-theme.css");
const SCALAR_CDN_URL: &str = "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.62.9";
const INITIAL_THEME_SCRIPT: &str = r#"
(() => {
const storageKey = "sbol-lab:theme";
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)");
const readPreference = () => {
try {
const stored = localStorage.getItem(storageKey);
if (stored === "light" || stored === "dark" || stored === "system") {
return stored;
}
} catch (_) {
// A blocked localStorage should not prevent the reference from loading.
}
return "system";
};
const applyRegistryTheme = () => {
const preference = readPreference();
const darkMode =
preference === "dark" ||
(preference === "system" && systemTheme.matches);
window.sbolDocsDarkMode = darkMode;
document.documentElement.classList.toggle("dark", darkMode);
document.documentElement.style.colorScheme = darkMode ? "dark" : "light";
document.body.classList.toggle("dark-mode", darkMode);
document.body.classList.toggle("light-mode", !darkMode);
};
applyRegistryTheme();
systemTheme.addEventListener("change", () => {
if (readPreference() === "system") applyRegistryTheme();
});
window.addEventListener("storage", (event) => {
if (event.key === storageKey || event.key === null) applyRegistryTheme();
});
})();
"#;
fn docs_header(subtitle: &str) -> String {
format!(
r#"<header class="sbol-docs-header">
<a class="sbol-docs-brand" href="/" aria-label="Back to SBOL DB">
<span class="sbol-docs-mark" aria-hidden="true">
<svg viewBox="0 0 45 45" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<path d="m 29.000111,5.2464081 8.5,7.4999999 -8.5,7.3333" />
<path d="m 7.5001114,39.746408 0,-27 28.9999996,0" />
</svg>
</span>
<span class="sbol-docs-title-group">
<span class="sbol-docs-title">SBOL DB</span>
<span class="sbol-docs-subtitle">{subtitle}</span>
</span>
</a>
<a class="sbol-docs-back" href="/">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="m15 18-6-6 6-6" />
</svg>
<span class="sbol-docs-back-label">Back to registry</span>
</a>
</header>"#
)
}
pub(crate) fn render_scalar_docs_page(
title: &str,
subtitle: &str,
reference_markup: &str,
mount_script: &str,
) -> String {
let header = docs_header(subtitle);
format!(
r##"<!doctype html>
<html lang="en">
<head>
<title>{title}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#17695e" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<style>
{SCALAR_THEME_CSS}
</style>
</head>
<body data-sbol-docs-shell>
<script>{INITIAL_THEME_SCRIPT}
</script>
{header}
{reference_markup}
<script src="{SCALAR_CDN_URL}"></script>
<script>{mount_script}
</script>
</body>
</html>
"##
)
}
const REFERENCE_MARKUP: &str = r#"<div id="app"></div>"#;
const MOUNT_SCRIPT: &str = r#"
Scalar.createApiReference(document.getElementById("app"), {
theme: "none",
layout: "modern",
darkMode: window.sbolDocsDarkMode,
forceDarkModeState: window.sbolDocsDarkMode ? "dark" : "light",
hideDarkModeToggle: true,
hideClientButton: false,
mcp: {
disabled: true
},
sources: [
{
title: "SBOL DB API",
slug: "native",
url: "/openapi.json",
default: true
},
{
title: "SynBioHub v1 compatibility API",
slug: "synbiohub-v1",
url: "/synbiohub/openapi.json"
},
{
title: "SynBioHub v2 API",
slug: "synbiohub-v2",
url: "/api/v2/openapi.json"
}
]
});
"#;
pub async fn openapi_json() -> impl IntoResponse {
([(CONTENT_TYPE, "application/json")], OPENAPI_JSON)
}
pub async fn synbiohub_openapi_json() -> impl IntoResponse {
([(CONTENT_TYPE, "application/json")], SYNBIOHUB_OPENAPI_JSON)
}
pub async fn docs_html() -> impl IntoResponse {
(
[(CONTENT_TYPE, "text/html; charset=utf-8")],
render_scalar_docs_page(
"SBOL DB API Reference",
"API reference",
REFERENCE_MARKUP,
MOUNT_SCRIPT,
),
)
}