feat(website): add homepage navigation and faster reading paths

This commit is contained in:
N0zoM1z0 2026-10-09 14:03:00 +08:00
parent 5b769d3a78
commit c94f06d98f
5 changed files with 266 additions and 162 deletions

View File

@ -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 <http://127.0.0.1:4173/>. 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

View File

@ -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();

View File

@ -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);
}

View File

@ -15,7 +15,7 @@
<script src="assets/home.js" defer></script>
<script type="module" src="assets/dino-demo.js"></script>
</head>
<body id="top">
<body class="home-page" id="top">
<a class="skip-link" href="#main">Skip to content</a>
<header class="site-header">
<div class="container header-inner">
@ -51,8 +51,33 @@
</a>
</div>
</header>
<details class="home-page-nav" data-home-nav open>
<summary class="container">
<span>On this page</span>
<span class="home-nav-current" data-home-nav-current aria-hidden="true"
>Set up REA</span
>
<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true">
<path d="m2 4 4 4 4-4" fill="none" stroke="currentColor" />
</svg>
</summary>
<nav class="home-nav-links" aria-label="On this page">
<div class="container">
<p class="home-nav-title">On this page</p>
<ul>
<li><a href="#setup" aria-current="location">Set up REA</a></li>
<li><a href="#what-is-re">What is RE?</a></li>
<li><a href="#how-rea-helps">How REA helps</a></li>
<li><a href="#dinosaur">Dinosaur game</a></li>
<li><a href="#calculator">Calculator</a></li>
<li><a href="#try-rea">Try REA</a></li>
<li><a href="#analyze">Analysis guides</a></li>
</ul>
</div>
</nav>
</details>
<main id="main">
<section class="container hero home-hero">
<section class="container hero home-hero" id="setup">
<div class="home-start-grid">
<div>
<p class="eyebrow">Reverse engineering, with your coding agent</p>
@ -64,7 +89,12 @@
<div class="actions">
<a class="button" href="#what-is-re">See how REA works ↓</a>
<a class="text-link" href="get-started/">Setup guide</a>
<a class="text-link" href="showcase/">Showcases →</a>
</div>
<p class="home-expert-shortcut">
Already know RE?
<a href="#analyze">Skip to analysis guides →</a>
</p>
</div>
<div class="home-setup">
<h2>Set up REA</h2>
@ -132,7 +162,7 @@
One question:
<strong>why does Calculator give 220 for 200 + 10%?</strong>
</p>
<figure class="home-role-comparison">
<figure class="home-role-comparison" id="how-rea-helps">
<div
class="manual-investigation step-comparison"
data-step-comparison
@ -830,10 +860,10 @@ SPEED: 6</code></pre>
</div>
</section>
<section class="section">
<section class="section" id="analyze">
<div class="container">
<div class="section-heading">
<h2>What REA can analyze</h2>
<h2>Analyze your software</h2>
<p>
Choose a guide for your binary, JavaScript app or browser session.
</p>
@ -866,161 +896,6 @@ SPEED: 6</code></pre>
</div>
</div>
</section>
<section class="section">
<div class="container feature-case">
<div>
<p class="eyebrow">Case study · Native</p>
<h2>Rebuilding DX-Ball</h2>
<p>
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.
</p>
<p>
<a class="text-link" href="showcase/dx-ball/"
>Follow the investigation</a
>
</p>
<p class="case-note">
Work so far covers boards, resources, brick hits, animations,
particles and bonus production. The playable game is still in
progress.
</p>
</div>
<dl class="stats case-stats">
<div>
<dt>55</dt>
<dd>functions in maintained C</dd>
</div>
<div>
<dt>45,380</dt>
<dd>original-x86 differential cases</dd>
</div>
<div>
<dt>33</dt>
<dd>complete functions with matching compiled bytes</dd>
</div>
</dl>
</div>
</section>
<section class="section">
<div class="container feature-case">
<div>
<p class="eyebrow">Case study · Electron</p>
<h2>Notion's Electron clipboard</h2>
<p>
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.
</p>
<p>
Short code excerpts show each handoff. A second figure explains
how an HTML marker connects ordinary clipboard text to Notion's
structured block data.
</p>
<a class="text-link" href="showcase/notion/"
>Follow the clipboard bridge</a
>
</div>
<figure class="workflow-figure case-preview">
<p class="figure-title">Clipboard IPC channel</p>
<div class="code-block">
<div class="code-header"><span>Preload · send</span></div>
<pre><code>invokerInMain(
<span class="notion-channel">"notion:clipboard:write"</span>
)</code></pre>
</div>
<div class="code-block">
<div class="code-header"><span>Main process · receive</span></div>
<pre><code>addListener(
<span class="notion-channel">"notion:clipboard:write"</span>,
handler
)</code></pre>
</div>
<figcaption>
Matching names give the investigation a path to follow.
</figcaption>
</figure>
</div>
</section>
<section class="section">
<div class="container feature-case">
<div>
<p class="eyebrow">Case study · PC-98 DOS</p>
<h2>TH04's aimed bullet rings</h2>
<p>
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.
</p>
<a class="text-link" href="showcase/th04/"
>See the bullet calculation</a
>
</div>
<figure class="workflow-figure case-preview th04-preview">
<p class="figure-title">Fixed and aimed rings</p>
<img
src="assets/figures/th04-bullet-ring.svg"
width="1000"
height="520"
alt="TH04's fixed and aimed 16-bullet rings side by side. The first bullet is highlighted in blue; adding the player direction rotates every bullet while preserving the spacing."
loading="lazy"
decoding="async"
/>
<figcaption>
Adding the player's direction rotates every bullet by the same
angle.
</figcaption>
</figure>
</div>
</section>
<section class="section">
<div class="container feature-case">
<div>
<p class="eyebrow">Case study · CTF</p>
<h2>Solve a CTF flag checker</h2>
<p>
Use REA to inspect an easy DownUnderCTF binary. Extract its
checking rules, solve for the flag and confirm that the original
program accepts it.
</p>
<a class="text-link" href="showcase/ctf/">See the CTF solution</a>
</div>
<figure class="workflow-figure case-preview">
<p class="figure-title">One check reveals one character</p>
<div class="code-block">
<div class="code-header">
<span>From REA's code and data</span>
</div>
<pre><code>Mask: skip 21, select 1, skip 14
Target: 55
code[21] = 55 → '7'</code></pre>
</div>
<figcaption>
Each mask selects characters whose codes must add up to a stored
number. This one selects a single character.
</figcaption>
</figure>
</div>
</section>
<section class="section">
<div class="container start-row">
<div>
<h2>What would you like to understand?</h2>
<p>
Connect REA to your coding agent, or run your first analysis from
the terminal.
</p>
</div>
<a class="button" href="get-started/">Get started</a>
</div>
</section>
</main>
<footer class="site-footer">
<div class="container footer-inner">

View File

@ -69,6 +69,13 @@ prompt and the ordinary setup command. Then explain what reverse engineering
means and what it lets someone do, before the worked examples.
Begin each example with its goal and the reason to inspect that program.
Name the actual evidence REA returned and what the agent did with it.
Give long pages a short table of contents with concrete section names. Preserve
the reading width: the homepage uses the side gutter when it has room,
and a collapsible sticky menu on smaller screens. Highlight the current
section, keep fragment links usable without JavaScript, and leave room above
the destination for the sticky control. Offer experienced readers a direct
route from setup to the analysis guides. Keep full case-study previews on the
Showcases page, linked from the opening actions.
Use short labels, selective bold text and the shared blue highlight to make
those facts visible when scanning. Keep source, interpretation and new code
attributed to their respective roles. On mobile, the demo can precede its