From b65db0be44c9a4033c5bcc40cd1bd5a6748938ed Mon Sep 17 00:00:00 2001 From: riricdev Date: Mon, 7 Sep 2026 15:34:23 +0200 Subject: [PATCH] =?UTF-8?q?fix(csp):=20docs=20+=20418=20sans=20style/scrip?= =?UTF-8?q?t=20inline=20=E2=80=94=20CSS/JS=20externalis=C3=A9s,=20header/f?= =?UTF-8?q?ooter=20inject=C3=A9s=20par=20components.js=20(#h/#f),=20playgr?= =?UTF-8?q?ound=20API=20d=C3=A9plac=C3=A9=20dans=20docs-playground.js=20et?= =?UTF-8?q?=20docs.css?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/docs.css | 160 +++++++++++++++++++++++++++++++++++ public/css/teapot.css | 104 +++++++++++++++++++++++ public/js/docs-playground.js | 15 ++++ public/js/teapot.js | 16 ++++ src/docs.ts | 54 ++---------- src/maxspeed-doc.md | 27 ------ src/server.ts | 46 ++-------- 7 files changed, 310 insertions(+), 112 deletions(-) create mode 100644 public/css/docs.css create mode 100644 public/css/teapot.css create mode 100644 public/js/docs-playground.js create mode 100644 public/js/teapot.js diff --git a/public/css/docs.css b/public/css/docs.css new file mode 100644 index 0000000..497320b --- /dev/null +++ b/public/css/docs.css @@ -0,0 +1,160 @@ +/* Page de documentation des API (docs/maxspeed etc.) */ +.docs-wrap { + flex: 1; + width: 100%; + min-height: 0; + display: flex; + gap: 32px; + align-items: flex-start; + max-width: 1180px; + margin: 0 auto; + padding: 40px clamp(1.25rem, 5vw, 2.5rem); +} +.docs-side { + flex: 0 0 240px; + position: sticky; + top: 24px; + font-size: 14px; +} +.docs-side h3 { + margin: 0 0 10px; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.08em; + opacity: 0.6; +} +.docs-side ul { + list-style: none; + margin: 0; + padding: 0; +} +.docs-side li { + margin: 4px 0; +} +.docs-side a { + color: inherit; + text-decoration: none; + opacity: 0.85; +} +.docs-side a:hover { + opacity: 1; + text-decoration: underline; +} +.docs-side a.active { + opacity: 1; + font-weight: 600; +} +.docs-body { + flex: 1 1 auto; + min-width: 0; + line-height: 1.7; + color: var(--text); +} +.docs-body h1 { + font-size: 1.9em; + border-bottom: 1px solid var(--border); + padding-bottom: 12px; +} +.docs-body h2 { + font-size: 1.35em; + margin-top: 2.2em; + border-bottom: 1px solid var(--border); + padding-bottom: 6px; +} +.docs-body h3 { + font-size: 1.1em; + margin-top: 1.6em; +} +.docs-body pre { + background: var(--bg-soft); + border: 1px solid var(--border); + border-radius: 8px; + padding: 14px; + overflow: auto; + font-size: 13px; + color: var(--text); +} +.docs-body code { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; +} +.docs-body :not(pre) > code { + background: var(--bg-soft); + padding: 2px 5px; + border-radius: 4px; + font-size: 0.9em; +} +.docs-body table { + border-collapse: collapse; + width: 100%; + margin: 16px 0; + font-size: 14px; +} +.docs-body th, +.docs-body td { + border: 1px solid var(--border); + padding: 8px 12px; + text-align: left; +} +.docs-body th { + background: var(--bg-soft); +} +.docs-body blockquote { + border-left: 3px solid var(--accent); + margin: 16px 0; + padding: 4px 16px; + opacity: 0.85; +} +@media (max-width: 760px) { + .docs-wrap { + flex-direction: column; + } + .docs-side { + position: static; + flex: auto; + } +} + +/* ===== Playground d'interrogation en direct (docs API) ===== */ + +.ms-playground { + background: var(--bg-soft); + border: 1px solid var(--border); + border-radius: 8px; + padding: 16px; +} +.ms-playground form { + display: flex; + gap: 12px; + flex-wrap: wrap; + align-items: end; +} +.ms-playground label { + display: flex; + flex-direction: column; + font-size: 12px; + gap: 4px; +} +.ms-playground input { + background: var(--bg); + color: inherit; + border: 1px solid var(--border); + border-radius: 4px; + padding: 6px 8px; + width: 130px; +} +.ms-playground button { + background: var(--bg-soft); + color: inherit; + border: 1px solid var(--border); + border-radius: 6px; + padding: 8px 14px; + cursor: pointer; +} +.ms-playground button:hover { + border-color: var(--accent); +} +.ms-out { + white-space: pre-wrap; + margin-top: 12px; + font-size: 13px; +} \ No newline at end of file diff --git a/public/css/teapot.css b/public/css/teapot.css new file mode 100644 index 0000000..f5b94ab --- /dev/null +++ b/public/css/teapot.css @@ -0,0 +1,104 @@ +/* Page easter egg 418 (I'm a Teapot — RFC 2324) */ +.tea-hero { + text-align: center; + padding: clamp(5rem, 12vh, 8rem) 0 4rem; + position: relative; +} +.tea-hero::before { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient(55% 45% at 50% 18%, var(--accent-soft) 0%, transparent 72%); + pointer-events: none; +} +.tea-hero > * { + position: relative; +} +.tea-hero h1 { + font-family: var(--font-head); + font-size: clamp(5rem, 18vw, 11rem); + font-weight: 900; + line-height: 1; + letter-spacing: -0.04em; + margin-bottom: 1.5rem; +} +.tea-hero .tea-sub { + color: var(--text-muted); + font-size: clamp(1rem, 2vw, 1.2rem); + max-width: 44ch; + margin: 0 auto 1.25rem; + line-height: 1.6; +} +.tea-stage { + position: relative; + width: min(340px, 80vw); + margin: 2rem auto; +} +.tea-pot { + display: block; + width: 100%; + color: var(--text); +} +.tea-steam { + position: absolute; + pointer-events: none; +} +.tea-steam span { + position: absolute; + bottom: 0; + display: block; + width: 14px; + height: 38px; + border-radius: 999px; + background: radial-gradient(circle at 50% 40%, var(--accent-soft) 0%, transparent 70%); + animation: tea-rise 3s ease-in infinite; +} +.tea-steam .ts1 { left: 44%; animation-delay: 0s; } +.tea-steam .ts2 { left: 52%; animation-delay: 1s; transform: scaleX(-1); } +.tea-steam .ts3 { left: 60%; animation-delay: 2s; } +@keyframes tea-rise { + 0% { opacity: 0; transform: translateY(10px) scale(0.7); } + 30% { opacity: 1; } + 100% { opacity: 0; transform: translateY(-120px) scale(1.15); } +} +@media (prefers-reduced-motion: reduce) { + .tea-steam span { animation: none; opacity: 0.35; } +} +.tea-actions { + display: flex; + justify-content: center; + gap: 1rem; + flex-wrap: wrap; +} +.tea-hint { + color: var(--text-muted); + opacity: 0.7; + font-size: 0.85rem; + margin-top: 2.25rem; +} +.tea-note { + color: var(--text-muted); + font-size: 0.9rem; + letter-spacing: 0.02em; +} +.tea-toast { + position: fixed; + left: 50%; + bottom: 2rem; + transform: translateX(-50%); + background: var(--bg-card); + border: 1px solid var(--border); + color: var(--text); + padding: 0.8rem 1.2rem; + border-radius: 10px; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35); + opacity: 0; + transition: opacity 0.25s ease; + z-index: 50; + pointer-events: none; + max-width: 90vw; + text-align: center; +} +.tea-toast.show { + opacity: 1; +} \ No newline at end of file diff --git a/public/js/docs-playground.js b/public/js/docs-playground.js new file mode 100644 index 0000000..9699a28 --- /dev/null +++ b/public/js/docs-playground.js @@ -0,0 +1,15 @@ +(function () { + var form = document.getElementById("ms-form") + var out = document.getElementById("ms-out") + if (!form || !out) return + form.addEventListener("submit", function (e) { + e.preventDefault() + var lat = document.getElementById("ms-lat").value + var lon = document.getElementById("ms-lon").value + out.textContent = "Chargement…" + fetch("/api/maxspeed/point?lat=" + encodeURIComponent(lat) + "&lon=" + encodeURIComponent(lon)) + .then(function (r) { return r.json() }) + .then(function (d) { out.textContent = JSON.stringify(d, null, 2) }) + .catch(function (err) { out.textContent = "Erreur: " + err }) + }) +})() \ No newline at end of file diff --git a/public/js/teapot.js b/public/js/teapot.js new file mode 100644 index 0000000..c24d057 --- /dev/null +++ b/public/js/teapot.js @@ -0,0 +1,16 @@ +(function () { + var toast = document.querySelector(".tea-toast"); + if (!toast) return; + var timer = null; + function spin(msg) { + toast.textContent = msg; + toast.classList.add("show"); + if (timer) clearTimeout(timer); + timer = setTimeout(function () { toast.classList.remove("show"); }, 3200); + } + document.querySelectorAll("[data-tea]").forEach(function (b) { + b.addEventListener("click", function () { + spin(b.getAttribute("data-toast") || ""); + }); + }); +})(); \ No newline at end of file diff --git a/src/docs.ts b/src/docs.ts index 9036261..b3177d9 100644 --- a/src/docs.ts +++ b/src/docs.ts @@ -24,56 +24,18 @@ function layout(title: string, description: string, body: string, sidebar: strin - + - - + + - - + + + - +
- + ` } diff --git a/src/maxspeed-doc.md b/src/maxspeed-doc.md index 2a973f8..0fee935 100644 --- a/src/maxspeed-doc.md +++ b/src/maxspeed-doc.md @@ -228,33 +228,6 @@ l'API en direct.
—
- - - - --- _Documentation générée automatiquement. Dernière vérification de la section diff --git a/src/server.ts b/src/server.ts index 7d375e0..8823652 100644 --- a/src/server.ts +++ b/src/server.ts @@ -612,30 +612,13 @@ function teapotPage(lang: "fr" | "en"): string { + - + - ${siteHeader(lang)} +
@@ -654,8 +637,8 @@ function teapotPage(lang: "fr" | "en"): string {

${text}

- - + +

\u2191 \u2191 \u2193 \u2193 \u2190 \u2192 \u2190 \u2192 B A

${note}

@@ -664,24 +647,9 @@ function teapotPage(lang: "fr" | "en"): string {
- ${siteFooter(lang)} +
- + ` }