Skip to content

Commit 5ce336e

Browse files
committed
docs: publish Runtime Atlas showcase
1 parent d94ae18 commit 5ce336e

18 files changed

Lines changed: 5256 additions & 0 deletions

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Runtime Atlas
22

3+
[![CI](https://github.com/OthmaneBlial/Runtime-Atlas/actions/workflows/ci.yml/badge.svg)](https://github.com/OthmaneBlial/Runtime-Atlas/actions/workflows/ci.yml)
4+
[![CodeQL](https://github.com/OthmaneBlial/Runtime-Atlas/actions/workflows/codeql.yml/badge.svg)](https://github.com/OthmaneBlial/Runtime-Atlas/actions/workflows/codeql.yml)
5+
[![Latest release](https://img.shields.io/github/v/release/OthmaneBlial/Runtime-Atlas?display_name=tag)](https://github.com/OthmaneBlial/Runtime-Atlas/releases/latest)
36
[![License: MIT](https://img.shields.io/badge/license-MIT-b9f227.svg)](LICENSE)
47
![Node.js 24.15.0](https://img.shields.io/badge/node-24.15.0-5FA04E?logo=nodedotjs)
58
![Privacy: local-first](https://img.shields.io/badge/privacy-local--first-6adce7.svg)
@@ -8,6 +11,8 @@ Runtime Atlas turns TypeScript declarations and recent trace evidence into a liv
811

912
It is a local-first developer tool: no account, hosted backend, analytics, or persistent telemetry database is included.
1013

14+
**[Explore the live showcase and documentation](https://othmaneblial.github.io/Runtime-Atlas/)**
15+
1116
![Runtime Atlas showing a completed checkout request across a source-backed application map](docs/assets/runtime-atlas-checkout.png)
1217

1318
## Why it is different

site/app.js

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
document.documentElement.classList.add("js");
2+
3+
const header = document.querySelector("[data-site-header]");
4+
5+
const updateHeader = () => {
6+
header?.classList.toggle("is-scrolled", window.scrollY > 12);
7+
};
8+
9+
updateHeader();
10+
window.addEventListener("scroll", updateHeader, { passive: true });
11+
12+
const revealItems = [...document.querySelectorAll("[data-reveal]")];
13+
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
14+
15+
if (!("IntersectionObserver" in window) || reducedMotion.matches) {
16+
revealItems.forEach((item) => item.classList.add("is-visible"));
17+
} else {
18+
const revealObserver = new IntersectionObserver(
19+
(entries, observer) => {
20+
for (const entry of entries) {
21+
if (!entry.isIntersecting) continue;
22+
entry.target.classList.add("is-visible");
23+
observer.unobserve(entry.target);
24+
}
25+
},
26+
{ rootMargin: "0px 0px -8%", threshold: 0.08 },
27+
);
28+
29+
revealItems.forEach((item) => revealObserver.observe(item));
30+
}
31+
32+
const copyStatus = document.querySelector("[data-copy-status]");
33+
let copyStatusTimer;
34+
35+
const showCopyStatus = (message) => {
36+
if (!copyStatus) return;
37+
window.clearTimeout(copyStatusTimer);
38+
copyStatus.textContent = message;
39+
copyStatus.classList.add("is-visible");
40+
copyStatusTimer = window.setTimeout(() => {
41+
copyStatus.classList.remove("is-visible");
42+
}, 1800);
43+
};
44+
45+
const fallbackCopy = (text) => {
46+
const input = document.createElement("textarea");
47+
input.value = text;
48+
input.setAttribute("readonly", "");
49+
input.style.position = "fixed";
50+
input.style.opacity = "0";
51+
document.body.append(input);
52+
input.select();
53+
const copied = document.execCommand("copy");
54+
input.remove();
55+
if (!copied) throw new Error("The browser did not accept the copy command");
56+
};
57+
58+
const copyText = async (text) => {
59+
if (navigator.clipboard?.writeText && window.isSecureContext) {
60+
await navigator.clipboard.writeText(text);
61+
return;
62+
}
63+
fallbackCopy(text);
64+
};
65+
66+
document.querySelectorAll(".copy-button").forEach((button) => {
67+
const initialLabel = button.textContent?.trim() || "COPY";
68+
69+
button.addEventListener("click", async () => {
70+
const targetId = button.dataset.copyTarget;
71+
const target = targetId ? document.getElementById(targetId) : null;
72+
const text = button.dataset.copyText ?? target?.innerText?.trim();
73+
74+
if (!text) {
75+
showCopyStatus("Nothing to copy");
76+
return;
77+
}
78+
79+
try {
80+
await copyText(text);
81+
button.textContent = "COPIED";
82+
button.classList.add("is-copied");
83+
showCopyStatus("Copied to clipboard");
84+
} catch {
85+
button.textContent = "SELECT TEXT";
86+
showCopyStatus("Copy unavailable — select the snippet");
87+
}
88+
89+
window.setTimeout(() => {
90+
button.textContent = initialLabel;
91+
button.classList.remove("is-copied");
92+
}, 1800);
93+
});
94+
});
95+
96+
const imageViewer = document.getElementById("image-viewer");
97+
const viewerImage = imageViewer?.querySelector("[data-viewer-image]");
98+
const viewerCaption = imageViewer?.querySelector("[data-viewer-caption]");
99+
100+
document.querySelectorAll(".media-open").forEach((trigger) => {
101+
trigger.addEventListener("click", () => {
102+
const source = trigger.dataset.image;
103+
const caption =
104+
trigger.dataset.caption ?? "Runtime Atlas product screenshot";
105+
if (!source || !imageViewer || !viewerImage || !viewerCaption) return;
106+
107+
if (typeof imageViewer.showModal !== "function") {
108+
window.open(source, "_blank", "noopener,noreferrer");
109+
return;
110+
}
111+
112+
viewerImage.src = source;
113+
viewerImage.alt = caption;
114+
viewerCaption.textContent = caption;
115+
imageViewer.showModal();
116+
});
117+
});
118+
119+
imageViewer
120+
?.querySelector("[data-dialog-close]")
121+
?.addEventListener("click", () => {
122+
imageViewer.close();
123+
});
124+
125+
imageViewer?.addEventListener("click", (event) => {
126+
if (event.target === imageViewer) imageViewer.close();
127+
});
128+
129+
const scrollableCandidates = [
130+
...document.querySelectorAll("pre, .table-wrap, .event-sequence"),
131+
];
132+
133+
const labelScrollableRegion = (element) => {
134+
if (element.getAttribute("aria-label")) return;
135+
const sectionTitle = element
136+
.closest("[data-doc-section]")
137+
?.querySelector("h2, h3")
138+
?.textContent?.trim();
139+
const codeTitle = element
140+
.closest(".code-shell")
141+
?.querySelector(".code-head span")
142+
?.textContent?.trim();
143+
const kind = element.matches("pre")
144+
? `${codeTitle || "Code sample"}`
145+
: element.matches(".table-wrap")
146+
? `${sectionTitle || "Data"} table`
147+
: sectionTitle || "Scrollable content";
148+
element.setAttribute("aria-label", `${kind}, scroll horizontally`);
149+
};
150+
151+
const updateScrollableRegions = () => {
152+
for (const element of scrollableCandidates) {
153+
const scrollable = element.scrollWidth > element.clientWidth + 1;
154+
if (scrollable) {
155+
element.dataset.scrollableRegion = "";
156+
element.setAttribute("role", "region");
157+
element.setAttribute("tabindex", "0");
158+
labelScrollableRegion(element);
159+
} else if (element.hasAttribute("data-scrollable-region")) {
160+
delete element.dataset.scrollableRegion;
161+
element.removeAttribute("role");
162+
element.removeAttribute("tabindex");
163+
if (
164+
element.getAttribute("aria-label")?.endsWith(", scroll horizontally")
165+
) {
166+
element.removeAttribute("aria-label");
167+
}
168+
}
169+
}
170+
};
171+
172+
updateScrollableRegions();
173+
if ("ResizeObserver" in window) {
174+
const scrollabilityObserver = new ResizeObserver(updateScrollableRegions);
175+
scrollableCandidates.forEach((element) =>
176+
scrollabilityObserver.observe(element),
177+
);
178+
} else {
179+
window.addEventListener("resize", updateScrollableRegions, { passive: true });
180+
}
181+
182+
const docsSearch = document.querySelector("[data-docs-search]");
183+
const docSections = [...document.querySelectorAll("[data-doc-section]")];
184+
const docLinks = [...document.querySelectorAll("[data-doc-link]")];
185+
const docsSummary = document.querySelector("[data-docs-summary]");
186+
const docsEmpty = document.querySelector("[data-docs-empty]");
187+
const clearDocsSearch = document.querySelector("[data-clear-docs-search]");
188+
189+
const normalizeSearch = (value) =>
190+
value
191+
.toLocaleLowerCase()
192+
.normalize("NFKD")
193+
.replace(/[\u0300-\u036f]/g, "")
194+
.trim();
195+
196+
const sectionForLink = (link) => {
197+
const id = link.getAttribute("href")?.replace(/^#/, "");
198+
return id ? document.getElementById(id) : null;
199+
};
200+
201+
const filterDocumentation = () => {
202+
if (!(docsSearch instanceof HTMLInputElement)) return;
203+
const query = normalizeSearch(docsSearch.value);
204+
const tokens = query.split(/\s+/).filter(Boolean);
205+
let visibleCount = 0;
206+
207+
for (const section of docSections) {
208+
const haystack = normalizeSearch(
209+
`${section.dataset.docTitle ?? ""} ${section.textContent ?? ""}`,
210+
);
211+
const matches = tokens.every((token) => haystack.includes(token));
212+
const isIntroduction = section.id === "introduction";
213+
section.hidden = Boolean(query) && !matches && !isIntroduction;
214+
if (!query || matches) visibleCount += 1;
215+
}
216+
217+
for (const link of docLinks) {
218+
const section = sectionForLink(link);
219+
link.hidden = Boolean(section?.hidden);
220+
}
221+
222+
if (docsSummary) {
223+
docsSummary.classList.toggle("is-visible", Boolean(query));
224+
docsSummary.textContent = query
225+
? `${visibleCount} section${visibleCount === 1 ? "" : "s"} matching “${docsSearch.value.trim()}”`
226+
: "";
227+
}
228+
229+
if (docsEmpty) docsEmpty.hidden = !query || visibleCount > 0;
230+
};
231+
232+
docsSearch?.addEventListener("input", filterDocumentation);
233+
234+
clearDocsSearch?.addEventListener("click", () => {
235+
if (!(docsSearch instanceof HTMLInputElement)) return;
236+
docsSearch.value = "";
237+
filterDocumentation();
238+
docsSearch.focus();
239+
});
240+
241+
document.addEventListener("keydown", (event) => {
242+
if (!(docsSearch instanceof HTMLInputElement)) return;
243+
const activeElement = document.activeElement;
244+
const isTyping =
245+
activeElement instanceof HTMLInputElement ||
246+
activeElement instanceof HTMLTextAreaElement ||
247+
activeElement?.getAttribute("contenteditable") === "true";
248+
249+
if (event.key === "/" && !isTyping) {
250+
event.preventDefault();
251+
docsSearch.focus();
252+
}
253+
254+
if (
255+
event.key === "Escape" &&
256+
activeElement === docsSearch &&
257+
docsSearch.value
258+
) {
259+
docsSearch.value = "";
260+
filterDocumentation();
261+
}
262+
});
263+
264+
if (docSections.length && "IntersectionObserver" in window) {
265+
const setActiveDocLink = (sectionId) => {
266+
for (const link of docLinks) {
267+
const active = link.getAttribute("href") === `#${sectionId}`;
268+
link.classList.toggle("is-active", active);
269+
if (active) link.setAttribute("aria-current", "location");
270+
else link.removeAttribute("aria-current");
271+
}
272+
};
273+
274+
const sectionObserver = new IntersectionObserver(
275+
(entries) => {
276+
const visible = entries
277+
.filter((entry) => entry.isIntersecting && !entry.target.hidden)
278+
.sort((a, b) => b.intersectionRatio - a.intersectionRatio);
279+
if (visible[0]?.target.id) setActiveDocLink(visible[0].target.id);
280+
},
281+
{ rootMargin: "-18% 0px -68%", threshold: [0, 0.1, 0.4] },
282+
);
283+
284+
docSections.forEach((section) => sectionObserver.observe(section));
285+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { createAtlas } from "@runtime-atlas/sdk";
2+
3+
const atlas = createAtlas({
4+
serviceName: "orders-api",
5+
collectorUrl: process.env.ATLAS_COLLECTOR_URL ?? "http://localhost:4319",
6+
headers: process.env.ATLAS_INGEST_TOKEN
7+
? { authorization: `Bearer ${process.env.ATLAS_INGEST_TOKEN}` }
8+
: undefined,
9+
onError: (error) =>
10+
console.warn("Atlas collector unavailable", error.message),
11+
});
12+
13+
export const ordersDatabase = atlas.database(
14+
{
15+
id: "db.orders-example",
16+
label: "Orders database",
17+
description: "Persists the customer order.",
18+
meta: { engine: "PostgreSQL" },
19+
},
20+
async () => ({ id: `ord_${Date.now()}` }),
21+
);
22+
23+
export const paymentApi = atlas.external(
24+
{
25+
id: "external.payments-example",
26+
label: "Payments API",
27+
description: "Authorizes payment with an external provider.",
28+
meta: { provider: "Stripe" },
29+
},
30+
async () => ({ authorized: true }),
31+
);
32+
33+
export const createOrder = atlas.service(
34+
{
35+
id: "service.orders-example",
36+
label: "Order service",
37+
description: "Coordinates payment and persistence.",
38+
},
39+
async () => {
40+
const payment = await paymentApi();
41+
const order = await ordersDatabase();
42+
return { payment, order };
43+
},
44+
);
45+
46+
export const createOrderRoute = atlas.route(
47+
{
48+
id: "route.orders-example",
49+
label: "POST /orders",
50+
meta: { method: "POST", path: "/orders" },
51+
},
52+
async () => createOrder(),
53+
);
54+
55+
// Call this from the real framework route handler.
56+
export const handleCreateOrder = () =>
57+
atlas.trace(
58+
{ method: "POST", path: "/orders", status: 201 },
59+
createOrderRoute,
60+
);

0 commit comments

Comments
 (0)