From c94f06d98f895eb8b6c5ffa5e999ef1d4bb5eea2 Mon Sep 17 00:00:00 2001
From: N0zoM1z0 <1705004398@qq.com>
Date: Fri, 9 Oct 2026 14:03:00 +0800
Subject: [PATCH] feat(website): add homepage navigation and faster reading
paths
---
website/README.md | 11 +-
website/public/assets/home.js | 74 ++++++++++++
website/public/assets/styles.css | 141 ++++++++++++++++++++++
website/public/index.html | 195 ++++++-------------------------
website/style-guide.md | 7 ++
5 files changed, 266 insertions(+), 162 deletions(-)
diff --git a/website/README.md b/website/README.md
index c011e6435..6f9af6887 100644
--- a/website/README.md
+++ b/website/README.md
@@ -5,7 +5,7 @@
> — N0zoM1z0
An English static website with explanatory figures, worked guides and DX-Ball, Notion, TH04 and CTF investigations.
-The public files are in `website/public/`. The site uses HTML, CSS and a small
+The public files are in `website/public/`. The site uses HTML, CSS and small
scripts for copying code, following code comparisons and playing the dinosaur
speed reconstruction. Python
packages the downloadable example; there is no frontend bundler or npm dependency.
@@ -26,7 +26,7 @@ Open . Refresh the browser after editing a file.
## Pages
-- `public/index.html`: compact setup, reverse-engineering definition and purpose, manual/agent comparison, dinosaur and Calculator examples, and case-study overviews.
+- `public/index.html`: compact setup, reverse-engineering definition and purpose, manual/agent comparison, dinosaur and Calculator examples, and analysis-guide links.
- `public/examples/dino-lab/index.html`: adjustable-speed mini-game, recovered rule, original-game check and browser analysis steps.
- `public/first-investigation/index.html`: a guided Notes export investigation, from setup to a checked CSV prediction.
- `public/showcase/index.html`: the case-study index.
@@ -100,6 +100,13 @@ and complete private Evidence records are not website assets.
The homepage starts with a short installation prompt and the ordinary setup
command, then explains what reverse engineering is and why someone would use
it. The manual/agent comparison introduces REA’s role before the examples.
+Its opening links directly to Showcases and lets experienced readers skip to
+the analysis guides. Case-study previews live on the Showcases page.
+A right-side table of contents stays visible at widths of 1440px and above.
+On narrower screens it becomes a sticky, native disclosure; selecting a link
+closes the menu and focuses the destination. The homepage script follows
+the reading position and marks the current link. Without JavaScript, the
+menu stays open and its fragment links remain usable.
The comparison uses the same Calculator question on both sides: three
selectable manual steps pair original instructions with an analyst illustration
and thought bubbles; the agent side begins with “One prompt.” and a copyable
diff --git a/website/public/assets/home.js b/website/public/assets/home.js
index 27cd1b32f..c5e102da6 100644
--- a/website/public/assets/home.js
+++ b/website/public/assets/home.js
@@ -62,5 +62,79 @@ function initializeExpertAssembly() {
}
}
+function initializeHomeNavigation() {
+ const menu = document.querySelector("[data-home-nav]");
+ if (menu === null) return;
+ const currentLabel = menu.querySelector("[data-home-nav-current]");
+ if (currentLabel === null) return;
+ const wideScreen = window.matchMedia("(min-width: 1440px)");
+ const entries = Array.from(menu.querySelectorAll('a[href^="#"]'))
+ .map((link) => ({
+ link,
+ target: document.getElementById(link.getAttribute("href").slice(1)),
+ }))
+ .filter((entry) => entry.target !== null);
+ if (entries.length === 0) return;
+
+ const updatePosition = () => {
+ const offset = wideScreen.matches ? 40 : 88;
+ let current = entries[0];
+ entries.forEach((entry) => {
+ if (entry.target.getBoundingClientRect().top <= offset) current = entry;
+ });
+ if (
+ window.scrollY + window.innerHeight >=
+ document.documentElement.scrollHeight - 2
+ ) {
+ const selected = entries.find(
+ (entry) => entry.link.hash === window.location.hash,
+ );
+ const selectedTop = selected?.target.getBoundingClientRect().top;
+ current =
+ selectedTop !== undefined &&
+ selectedTop >= 0 &&
+ selectedTop < window.innerHeight
+ ? selected
+ : entries[entries.length - 1];
+ }
+ entries.forEach((entry) => {
+ if (entry === current)
+ entry.link.setAttribute("aria-current", "location");
+ else entry.link.removeAttribute("aria-current");
+ });
+ currentLabel.textContent = current.link.textContent;
+ };
+ const updateLayout = () => {
+ menu.open = wideScreen.matches;
+ updatePosition();
+ };
+ let scheduled = false;
+ const schedulePosition = () => {
+ if (scheduled) return;
+ scheduled = true;
+ window.requestAnimationFrame(() => {
+ scheduled = false;
+ updatePosition();
+ });
+ };
+ entries.forEach(({ link, target }) => {
+ link.addEventListener("click", () => {
+ if (!wideScreen.matches) menu.open = false;
+ const heading = target.querySelector("h2") ?? target;
+ heading.setAttribute("tabindex", "-1");
+ window.requestAnimationFrame(() =>
+ heading.focus({ preventScroll: true }),
+ );
+ });
+ });
+ wideScreen.addEventListener("change", updateLayout);
+ window.addEventListener("scroll", schedulePosition, { passive: true });
+ window.addEventListener("resize", schedulePosition, { passive: true });
+ window.addEventListener("hashchange", schedulePosition);
+ window.addEventListener("load", schedulePosition);
+ updateLayout();
+}
+
initializeExpertAssembly();
initializePercentDemo();
+initializeHomeNavigation();
diff --git a/website/public/assets/styles.css b/website/public/assets/styles.css
index 3bcc84d9e..fc9850f69 100644
--- a/website/public/assets/styles.css
+++ b/website/public/assets/styles.css
@@ -1218,6 +1218,137 @@ pre code {
border: 0;
}
+.home-page-nav {
+ position: sticky;
+ top: 0;
+ z-index: 6;
+ background: var(--page);
+ border-bottom: 1px solid var(--line);
+}
+.home-page-nav > summary {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ min-height: 52px;
+ color: var(--ink);
+ font-size: 0.8rem;
+ font-weight: 600;
+ list-style: none;
+ cursor: pointer;
+}
+.home-page-nav > summary::-webkit-details-marker {
+ display: none;
+}
+.home-nav-current {
+ margin-left: auto;
+ color: var(--muted);
+ font-weight: 400;
+}
+.home-page-nav[open] > summary svg {
+ transform: rotate(180deg);
+}
+.home-nav-links {
+ position: absolute;
+ inset: 100% 0 auto;
+ max-height: calc(100svh - 5rem);
+ overflow-y: auto;
+ padding-block: 0.4rem 1rem;
+ background: var(--page);
+ border-bottom: 1px solid var(--line);
+}
+.home-nav-title {
+ display: none;
+}
+.home-nav-links ul {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 0.2rem 1rem;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+.home-nav-links li + li {
+ margin: 0;
+}
+.home-nav-links a {
+ display: flex;
+ align-items: center;
+ min-height: 44px;
+ padding: 0.5rem 0.65rem;
+ border-left: 2px solid transparent;
+ color: var(--muted);
+ font-size: 0.825rem;
+ line-height: 1.5;
+ text-decoration: none;
+}
+.home-nav-links a:hover {
+ color: var(--ink);
+ background: var(--soft);
+}
+.home-nav-links a[aria-current="location"] {
+ border-left-color: var(--accent);
+ background: #f1f5fa;
+ color: var(--accent);
+ font-weight: 600;
+}
+.home-page section[id],
+.home-role-comparison[id] {
+ scroll-margin-top: 5.25rem;
+}
+.home-expert-shortcut {
+ margin-block: 1.5rem 0;
+ color: var(--muted);
+ font-size: 0.85rem;
+}
+.home-expert-shortcut a {
+ white-space: nowrap;
+}
+@media (min-width: 1440px) {
+ .home-page-nav {
+ position: fixed;
+ top: 7.5rem;
+ right: max(1.5rem, calc((100vw - 1080px) / 2 - 156px));
+ width: 132px;
+ max-height: calc(100svh - 9rem);
+ overflow-y: auto;
+ border: 0;
+ z-index: 2;
+ }
+ .home-page-nav > summary {
+ display: none;
+ }
+ .home-nav-links {
+ position: static;
+ max-height: none;
+ overflow: visible;
+ padding: 0;
+ border: 0;
+ }
+ .home-nav-links .container {
+ width: 100%;
+ }
+ .home-nav-title {
+ display: block;
+ margin-bottom: 0.7rem;
+ padding-left: 0.75rem;
+ color: var(--muted);
+ font-size: 0.75rem;
+ font-weight: 600;
+ }
+ .home-nav-links ul {
+ display: block;
+ border-left: 1px solid var(--line);
+ }
+ .home-nav-links a {
+ margin-left: -1px;
+ font-size: 0.78rem;
+ }
+ .home-page section[id],
+ .home-role-comparison[id] {
+ scroll-margin-top: 2rem;
+ }
+}
+
.home-start-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
@@ -1765,6 +1896,16 @@ pre code {
}
}
@media (max-width: 640px) {
+ .home-start-grid .actions {
+ display: grid;
+ grid-template-columns: max-content max-content;
+ justify-content: start;
+ gap: 0.9rem 1.4rem;
+ }
+ .home-start-grid .actions .button {
+ grid-column: 1 / -1;
+ justify-self: start;
+ }
.expert-analysis-grid {
grid-template-columns: minmax(0, 1fr);
}
diff --git a/website/public/index.html b/website/public/index.html
index 26f8d2b0a..e3c9a7b0e 100644
--- a/website/public/index.html
+++ b/website/public/index.html
@@ -15,7 +15,7 @@
-
+
Skip to content
+
+
+ On this page
+ Set up REA
+
+
+
+
+
+
+
+
-
+
Set up REA
@@ -132,7 +162,7 @@
One question:
why does Calculator give 220 for 200 + 10%?
-
+
-
+
-
What REA can analyze
+
Analyze your software
Choose a guide for your binary, JavaScript app or browser session.
@@ -866,161 +896,6 @@ SPEED: 6
-
-
-
-
-
Case study · Native
-
Rebuilding DX-Ball
-
- DX-Ball is a 1996 Windows game. The project is reconstructing it
- in C, using REA to inspect the executable and independent tests to
- check the recovered behavior.
-
-
- Follow the investigation
-
-
- Work so far covers boards, resources, brick hits, animations,
- particles and bonus production. The playable game is still in
- progress.
-
-
-
-
-
55
- functions in maintained C
-
-
-
45,380
- original-x86 differential cases
-
-
-
33
- complete functions with matching compiled bytes
-
-
-
-
-
-
-
-
-
Case study · Electron
-
Notion's Electron clipboard
-
- A copy operation crosses from the page to Electron's main process.
- Inspect Notion's packaged JavaScript with REA, then follow one
- channel through the wrappers to the system clipboard.
-
-
- Short code excerpts show each handoff. A second figure explains
- how an HTML marker connects ordinary clipboard text to Notion's
- structured block data.
-
-
Follow the clipboard bridge
-
-
- Clipboard IPC channel
-
-
-
invokerInMain(
- "notion:clipboard:write"
-)
-
-
-
-
addListener(
- "notion:clipboard:write" ,
- handler
-)
-
-
- Matching names give the investigation a path to follow.
-
-
-
-
-
-
-
-
-
Case study · PC-98 DOS
-
TH04's aimed bullet rings
-
- Inspect a 16-bit DOS function with REA. Follow its angle
- calculation from original instructions to readable C++, then see
- how adding the player's direction turns a ring of bullets.
-
-
See the bullet calculation
-
-
- Fixed and aimed rings
-
-
- Adding the player's direction rotates every bullet by the same
- angle.
-
-
-
-
-
-
-
-
-
Case study · CTF
-
Solve a CTF flag checker
-
- Use REA to inspect an easy DownUnderCTF binary. Extract its
- checking rules, solve for the flag and confirm that the original
- program accepts it.
-
-
See the CTF solution
-
-
- One check reveals one character
-
-
-
Mask: skip 21, select 1, skip 14
-Target: 55
-
-code[21] = 55 → '7'
-
-
- Each mask selects characters whose codes must add up to a stored
- number. This one selects a single character.
-
-
-
-
-
-
-
-
-
What would you like to understand?
-
- Connect REA to your coding agent, or run your first analysis from
- the terminal.
-
-
-
Get started
-
-