feat(website): add search metadata and automatic sitemap

Prefer rea.tools in page canonicals and search listings. Generate the sitemap from current HTML indexing directives and render a shared PNG preview from SVG before verification and manual deployment.

Refs #1283
This commit is contained in:
N0zoM1z0 committed 2026-10-09 21:46:05 +08:00
1 parent 0c6852b392
commit b8851cb977
28 files changed
+1123 -38

No files matched your search

+17 -5
View File
@@ -5,8 +5,7 @@ on:
branches: [main, "release/*"]
paths:
- "website/**"
- "scripts/verify-website.py"
- "scripts/prepare-website.py"
- "scripts/*website*.py"
- ".github/workflows/**"
workflow_dispatch:
@@ -21,11 +20,24 @@ jobs:
check:
name: Website checks
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 10
steps:
- name: Check out the repository
uses: actions/checkout@v4
- name: Prepare the example download
- name: Set up Python for website assets
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: website/requirements.txt
- name: Install sharing-image dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y libcairo2 fonts-dejavu-core
python3 -m pip install -r website/requirements.txt
- name: Prepare website assets
run: python3 scripts/prepare-website.py
- name: Check website links and publishing configuration
- name: Check website links, metadata and publishing configuration
run: python3 scripts/verify-website.py
- name: Check sitemap discovery and metadata regressions
run: python3 scripts/test-website.py
+14 -3
View File
@@ -23,7 +23,7 @@ jobs:
prepare:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 10
permissions:
contents: read
pages: read
@@ -48,9 +48,20 @@ jobs:
fi
- name: Check out the repository
uses: actions/checkout@v4
- name: Prepare the example download
- name: Set up Python for website assets
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: website/requirements.txt
- name: Install sharing-image dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y libcairo2 fonts-dejavu-core
python3 -m pip install -r website/requirements.txt
- name: Prepare website assets
run: python3 scripts/prepare-website.py
- name: Check website links and publishing configuration
- name: Check website links, metadata and publishing configuration
run: python3 scripts/verify-website.py
- name: Record the published commit
run: |
+3
View File
@@ -50,6 +50,9 @@ Thumbs.db
# Generated website download
/website/public/examples/notes-example.zip
/website/public/deployment.json
/website/public/sitemap.xml
/website/public/assets/social-card.png
/website/.venv/
# Local Cloudflare development state
/website/.wrangler/
+75 -2
View File
@@ -1,10 +1,16 @@
"""Package the public Notes example for the static website."""
"""Prepare downloads, the sitemap and the sharing image for the static website."""
from html.parser import HTMLParser
from io import BytesIO
from pathlib import Path
import re
import sys
from urllib.parse import quote
import xml.etree.ElementTree as ET
from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
SITE_ORIGIN = "https://rea.tools"
EXAMPLE_FILES = (
"package.json",
"main.js",
@@ -15,6 +21,70 @@ EXAMPLE_FILES = (
)
class IndexPolicy(HTMLParser):
"""Read indexing directives from the authored HTML head."""
def __init__(self):
super().__init__()
self.in_head = False
self.noindex = False
def handle_starttag(self, tag, attrs):
if tag == "head":
self.in_head = True
if tag == "meta" and self.in_head:
attributes = dict(attrs)
if (attributes.get("name") or "").lower() in ("robots", "googlebot"):
directives = re.split(
r"[\s,]+", (attributes.get("content") or "").lower()
)
self.noindex |= bool(set(directives) & {"noindex", "none"})
def handle_endtag(self, tag):
if tag == "head":
self.in_head = False
def prepare_sitemap(site):
"""Discover HTML routes automatically, excluding pages marked noindex."""
namespace = "http://www.sitemaps.org/schemas/sitemap/0.9"
ET.register_namespace("", namespace)
sitemap = ET.Element(f"{{{namespace}}}urlset")
for path in sorted(site.rglob("*.html")):
policy = IndexPolicy()
policy.feed(path.read_text(encoding="utf-8"))
if policy.noindex:
continue
route = path.relative_to(site).as_posix()
if path.name == "index.html":
route = route.removesuffix("index.html")
entry = ET.SubElement(sitemap, f"{{{namespace}}}url")
ET.SubElement(
entry, f"{{{namespace}}}loc"
).text = f"{SITE_ORIGIN}/{quote(route, safe='/')}"
ET.indent(sitemap, space=" ")
ET.ElementTree(sitemap).write(
site / "sitemap.xml", encoding="utf-8", xml_declaration=True
)
print(f"Prepared sitemap.xml ({len(sitemap)} indexable pages).")
def prepare_sharing_image(site):
"""Rasterize the maintained SVG for clients that need a PNG preview."""
try:
import cairosvg
except (ImportError, OSError) as error:
sys.exit(
f"Cannot prepare sharing image: {error}. Install website/requirements.txt and native Cairo; see website/README.md."
)
source = site / "assets/social-card.svg"
destination = site / "assets/social-card.png"
cairosvg.svg2png(
url=str(source), write_to=str(destination), output_width=1200, output_height=630
)
print("Prepared assets/social-card.png (1200 × 630).")
def prepare_notes_example(site):
"""Create a ZIP with public source only and reproducible metadata."""
source = site / "examples/notes-electron"
@@ -37,4 +107,7 @@ def prepare_notes_example(site):
if __name__ == "__main__":
root = Path(__file__).resolve().parent.parent
prepare_notes_example(root / "website/public")
site = root / "website/public"
prepare_sharing_image(site)
prepare_sitemap(site)
prepare_notes_example(site)
+130
View File
@@ -0,0 +1,130 @@
"""Exercise automatic route discovery and mistakes that break search metadata."""
import contextlib
import importlib.util
import io
from pathlib import Path
import shutil
import tempfile
import unittest
import xml.etree.ElementTree as ET
ROOT = Path(__file__).resolve().parent.parent
def load_script(name):
"""Load a repository script without executing its command-line entrypoint."""
spec = importlib.util.spec_from_file_location(name, ROOT / "scripts" / f"{name}.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
PREPARE = load_script("prepare-website")
VERIFY = load_script("verify-website")
class WebsiteSearchChecks(unittest.TestCase):
"""Use authored pages and temporary fixtures to check the publishing boundary."""
def test_sitemap_tracks_added_moved_removed_and_nonindexable_pages(self):
with tempfile.TemporaryDirectory() as directory:
site = Path(directory)
def page(name, head="", body=""):
path = site / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(f"<html><head>{head}</head><body>{body}</body></html>")
return path
page("index.html")
nested = page("guides/new/index.html")
page(
"examples/fixture/index.html",
'<meta name="robots" content="NOINDEX, follow">',
)
page("draft.html", '<meta name="googlebot" content="none">')
page("code & notes.html")
page("visible/index.html", body='<meta name="robots" content="noindex">')
(site / "asset.json").write_text("{}")
def urls():
with contextlib.redirect_stdout(io.StringIO()):
PREPARE.prepare_sitemap(site)
sitemap = ET.parse(site / "sitemap.xml")
return {node.text for node in sitemap.findall(".//{*}loc")}
self.assertEqual(
urls(),
{
"https://rea.tools/",
"https://rea.tools/guides/new/",
"https://rea.tools/code%20%26%20notes.html",
"https://rea.tools/visible/",
},
)
nested.rename(site / "moved.html")
self.assertIn("https://rea.tools/moved.html", urls())
self.assertNotIn("https://rea.tools/guides/new/", urls())
(site / "moved.html").unlink()
self.assertNotIn("https://rea.tools/moved.html", urls())
def test_verifier_rejects_wrong_canonicals_stale_sitemaps_and_missing_previews(
self,
):
cases = (
(
"index.html",
'rel="canonical" href="https://rea.tools/"',
'rel="canonical" href="https://morluto.github.io/rea/"',
"head canonical",
),
(
"showcase/aegis/index.html",
'rel="canonical" href="https://rea.tools/showcase/aegis/"',
'rel="canonical" href="https://rea.tools/"',
"head canonical",
),
(
"examples/notes-electron/index.html",
'content="noindex"',
'content="index"',
"must have a head noindex",
),
(
"sitemap.xml",
"https://rea.tools/faq/",
"https://rea.tools/deleted/",
"each indexable page once",
),
(
"faq/index.html",
'name="twitter:card" content="summary_large_image"',
'name="twitter:card" content="summary"',
"twitter:card",
),
)
for name, old, new, message in cases:
with self.subTest(name=name), tempfile.TemporaryDirectory() as directory:
site = Path(directory) / "public"
shutil.copytree(ROOT / "website/public", site)
path = site / name
source = path.read_text()
self.assertIn(old, source)
path.write_text(source.replace(old, new, 1))
self.assertTrue(
any(message in error for error in VERIFY.check_site(site)[3])
)
with tempfile.TemporaryDirectory() as directory:
site = Path(directory) / "public"
shutil.copytree(ROOT / "website/public", site)
(site / "assets/social-card.png").unlink()
self.assertIn(
"Social preview PNG is missing; run scripts/prepare-website.py",
VERIFY.check_site(site)[3],
)
if __name__ == "__main__":
unittest.main()
+219 -10
View File
@@ -1,14 +1,19 @@
"""Check local website references and the sole GitHub Pages publisher."""
"""Check website references, search metadata and the sole production publisher."""
from html.parser import HTMLParser
from pathlib import Path
import re
import struct
import sys
from urllib.parse import unquote, urlsplit
from urllib.parse import quote, unquote, urlsplit
import xml.etree.ElementTree as ET
from zipfile import BadZipFile, ZipFile
SITE_ORIGIN = "https://rea.tools"
NOTES_FIXTURES = {"examples/notes-electron/index.html", "examples/notes-web/index.html"}
class HtmlReferences(HTMLParser):
"""Collect document IDs and references without fetching external URLs."""
@@ -17,9 +22,42 @@ class HtmlReferences(HTMLParser):
self.ids = set()
self.references = []
self.duplicate_ids = []
self.in_head = False
self.in_title = False
self.titles = []
self.meta = {}
self.properties = {}
self.canonicals = []
self.lang = None
self.h1_count = 0
def handle_starttag(self, tag, attrs):
attributes = dict(attrs)
if tag == "html":
self.lang = attributes.get("lang")
if tag == "h1":
self.h1_count += 1
if tag == "head":
self.in_head = True
if self.in_head:
if tag == "title":
self.titles.append("")
self.in_title = True
if (
tag == "link"
and "canonical" in (attributes.get("rel") or "").lower().split()
):
self.canonicals.append(attributes.get("href") or "")
if tag == "meta":
for attribute, collection in (
("name", self.meta),
("property", self.properties),
):
key = attributes.get(attribute)
if key:
collection.setdefault(key.lower(), []).append(
attributes.get("content") or ""
)
identifier = attributes.get("id")
if identifier is not None:
if identifier in self.ids:
@@ -30,6 +68,154 @@ class HtmlReferences(HTMLParser):
if reference is not None:
self.references.append(reference)
def handle_endtag(self, tag):
if tag == "head":
self.in_head = False
if tag == "title":
self.in_title = False
def handle_data(self, data):
if self.in_title:
self.titles[-1] += data
def noindex(self):
"""Recognize directives that exclude this page from Google's index."""
directives = ",".join(
self.meta.get("robots", []) + self.meta.get("googlebot", [])
).lower()
return bool(set(re.split(r"[\s,]+", directives)) & {"noindex", "none"})
def check_search_metadata(site, pages):
"""Verify route-specific canonicals, sharing assets and the generated sitemap."""
errors = []
indexable = set()
titles = set()
for path, page in pages.items():
label = path.relative_to(site).as_posix()
title = page.titles[0].strip() if len(page.titles) == 1 else ""
if not title:
errors.append(f"{label}: needs exactly one nonempty head title")
if page.lang != "en" or page.h1_count != 1:
errors.append(f"{label}: needs lang=en and exactly one h1")
if label in NOTES_FIXTURES and not page.noindex():
errors.append(
f"{label}: example application must have a head noindex directive"
)
if page.noindex():
continue
route = (
path.relative_to(site).parent.as_posix()
if path.name == "index.html"
else label
)
route = "" if route == "." else route
url = f"{SITE_ORIGIN}/{quote(route, safe='/')}" + (
"/" if route and path.name == "index.html" else ""
)
indexable.add(url)
if page.canonicals != [url]:
errors.append(f"{label}: needs exactly one head canonical for {url}")
if title in titles:
errors.append(f"{label}: duplicate search title {title!r}")
titles.add(title)
descriptions = page.meta.get("description", [])
description = descriptions[0].strip() if len(descriptions) == 1 else ""
if not description:
errors.append(f"{label}: needs exactly one nonempty head description")
expected_og = {
"og:type": "website",
"og:site_name": "REA",
"og:title": title,
"og:description": description,
"og:url": url,
"og:image": f"{SITE_ORIGIN}/assets/social-card.png",
"og:image:type": "image/png",
"og:image:width": "1200",
"og:image:height": "630",
}
expected_twitter = {
"twitter:card": "summary_large_image",
"twitter:title": title,
"twitter:description": description,
"twitter:image": expected_og["og:image"],
}
for collection, expected in (
(page.properties, expected_og),
(page.meta, expected_twitter),
):
for key, value in expected.items():
if collection.get(key) != [value]:
errors.append(
f"{label}: {key} must appear once and match {value!r}"
)
for collection, key in (
(page.properties, "og:image:alt"),
(page.meta, "twitter:image:alt"),
):
values = collection.get(key, [])
if len(values) != 1 or not values[0].strip():
errors.append(f"{label}: needs exactly one nonempty {key}")
sitemap_path = site / "sitemap.xml"
try:
sitemap = ET.parse(sitemap_path).getroot()
namespace = "{http://www.sitemaps.org/schemas/sitemap/0.9}"
urls = [entry.findtext(f"{namespace}loc") for entry in sitemap]
if sitemap.tag != f"{namespace}urlset" or any(
entry.tag != f"{namespace}url" for entry in sitemap
):
errors.append("sitemap.xml: needs a sitemap-protocol urlset")
if len(urls) != len(set(urls)) or set(urls) != indexable:
errors.append(
"sitemap.xml: must contain each indexable page once, using its rea.tools URL"
)
if any(
len(entry) != 1 or entry[0].tag != f"{namespace}loc" for entry in sitemap
):
errors.append(
"sitemap.xml: keep entries to loc; do not invent lastmod or add priority/changefreq"
)
except (OSError, ET.ParseError) as error:
errors.append(
f"sitemap.xml: cannot read sitemap ({error}); run scripts/prepare-website.py"
)
robots_path = site / "robots.txt"
if not robots_path.is_file():
errors.append("robots.txt: authored crawl rules are missing")
else:
directives = []
for line in robots_path.read_text(encoding="utf-8").splitlines():
value = line.split("#", 1)[0].strip()
if value:
key, separator, content = value.partition(":")
directives.append(
(key.lower(), content.strip()) if separator else (value, "")
)
expected = [
("user-agent", "*"),
("allow", "/"),
("sitemap", f"{SITE_ORIGIN}/sitemap.xml"),
]
if directives != expected:
errors.append(
"robots.txt: expected crawl access and one absolute rea.tools sitemap directive"
)
image = site / "assets/social-card.png"
if not image.is_file():
errors.append("Social preview PNG is missing; run scripts/prepare-website.py")
else:
header = image.read_bytes()[:24]
if (
len(header) != 24
or header[:16] != b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR"
or struct.unpack(">II", header[16:24]) != (1200, 630)
):
errors.append("Social preview must be a 1200 × 630 PNG")
return len(indexable), errors
def check_site(site):
"""Check assets, HTML fragment destinations and SVG syntax."""
@@ -63,7 +249,11 @@ def check_site(site):
target /= "index.html"
if not target.is_file():
errors.append(f"{label}: missing local target: {reference}")
elif target in pages and url.fragment and unquote(url.fragment) not in pages[target].ids:
elif (
target in pages
and url.fragment
and unquote(url.fragment) not in pages[target].ids
):
errors.append(f"{label}: missing HTML fragment: {reference}")
for path in sorted(site.rglob("*.svg")):
@@ -71,18 +261,23 @@ def check_site(site):
ET.parse(path)
except ET.ParseError as error:
errors.append(f"{path.relative_to(site)}: invalid SVG XML: {error}")
return len(pages), references, errors
indexable, metadata_errors = check_search_metadata(site, pages)
errors.extend(metadata_errors)
return len(pages), references, indexable, errors
def check_publisher(root):
"""Detect a second Pages action that could replace the website."""
expected = root / ".github/workflows/website-pages.yml"
workflows = root / ".github/workflows"
deploy_action = re.compile(r"^\s*-?\s*uses:\s*['\"]?actions/deploy-pages@", re.MULTILINE)
deploy_action = re.compile(
r"^\s*-?\s*uses:\s*['\"]?actions/deploy-pages@", re.MULTILINE
)
publishers = [
path
for path in sorted(workflows.iterdir())
if path.suffix in (".yml", ".yaml") and deploy_action.search(path.read_text(encoding="utf-8"))
if path.suffix in (".yml", ".yaml")
and deploy_action.search(path.read_text(encoding="utf-8"))
]
if publishers != [expected]:
actual = ", ".join(str(path.relative_to(root)) for path in publishers) or "none"
@@ -95,7 +290,14 @@ def check_example_archive(site):
source = site / "examples/notes-electron"
expected = {
f"notes-example/{name}": source / name
for name in ("package.json", "main.js", "preload.js", "renderer.js", "csv.js", "index.html")
for name in (
"package.json",
"main.js",
"preload.js",
"renderer.js",
"csv.js",
"index.html",
)
}
path = site / "examples/notes-example.zip"
if not path.is_file():
@@ -103,7 +305,9 @@ def check_example_archive(site):
try:
with ZipFile(path) as archive:
if sorted(archive.namelist()) != sorted(expected):
return ["Notes example ZIP must contain exactly the six files in notes-example/."]
return [
"Notes example ZIP must contain exactly the six files in notes-example/."
]
return [
f"Notes example ZIP has stale source: {name}"
for name, original in expected.items()
@@ -116,13 +320,18 @@ def check_example_archive(site):
def main():
"""Run the same checks locally, on website PRs and before publishing."""
root = Path(__file__).resolve().parent.parent
pages, references, errors = check_site(root / "website/public")
pages, references, indexable, errors = check_site(root / "website/public")
errors.extend(check_example_archive(root / "website/public"))
errors.extend(check_publisher(root))
if errors:
print("Website checks failed:\n" + "\n".join(errors), file=sys.stderr)
return 1
print(f"Verified {pages} HTML pages, {references} local references, SVG XML and the example ZIP.")
print(
f"Verified {pages} HTML pages, {references} local references, SVG XML and the example ZIP."
)
print(
f"Verified search/sharing metadata, robots.txt and the sitemap for {indexable} indexable pages."
)
print("website-pages.yml is the sole GitHub Pages publisher.")
return 0
+19 -2
View File
@@ -33,8 +33,8 @@ when the example files change.
The guide's `examples/notes-example.zip` is generated from exactly these six
source files, under the `notes-example/` folder. Packaging changes no file
contents. Website verification compares every ZIP entry to its source file and
rejects extra entries. Existing source digests and static findings remain valid
for the unzipped example.
rejects extra entries. The recorded digests below identify the original analysis
checkpoint; subsequent metadata changes and checks are recorded separately.
## First-investigation formatter check
@@ -82,6 +82,23 @@ The guide's follow-up CLI commands were also exercised against the real page:
source. Discovery Evidence: `ev_31abb33e7707d47625ac909ab9256c705d03a31ee6bcd643fa96730e72469162`. Script capture
Evidence: `ev_aeb777df405c09dc1bbdd4c185a5af2452abaa3cb493683c296c5afa97efbc11`.
## SEO metadata check — 9 October 2026
Both Notes HTML heads gained descriptions and `noindex` directives. The Electron
fixture was analyzed again with `rea-agents` 4.1.0 and Node.js 24.18.0 on Linux
x64. REA still reports four parsed JavaScript files, zero parse failures, one
exposed API member, one literal IPC channel, one renderer transmission, one main
handler and one paired transmission, with zero ambiguous transmissions.
- Evidence ID: `ev_3958d870ecd27c202e5decc97161f2ea19db8cc88f9f39ed0449bf1581df2f2a`.
- Root artifact SHA-256: `0ee325d4eaf7f94b62b2962044995fe1efe9f1d31411eb035118b19b1f56f7bb`.
- Current Electron HTML SHA-256: `457b22543aa956fe2cd005fc6bda42d2798eb05040b75c4cb4957e3b66d0ea09`.
- Current browser HTML SHA-256: `3b24b3307ded48055ce5a48f907544195bfde3285f82df919c54a6415ac11070`.
The JavaScript and data files retain their original digests. The generated ZIP
contains the updated HTML and the same JavaScript. Local browser checks exercise
the Notes CSV download at both root and `/rea/` paths.
Full local outputs and the browser verification harness are under
`/tmp/rea-website-guides/` in the development environment, not website assets.
+27
View File
@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">
<title id="title">REA — Find out how software works.</title>
<desc id="desc">Reverse engineering with your coding agent. REA connects your agent to analysis tools and returns evidence.</desc>
<rect width="1200" height="630" fill="#ffffff"/>
<g font-family="DejaVu Sans, sans-serif" fill="#20252b">
<text x="72" y="110" font-size="44" font-weight="700">REA</text>
<text x="1128" y="106" font-size="24" text-anchor="end" fill="#5b6570">rea.tools</text>
<path d="M72 142 H1128" stroke="#dce2e8" stroke-width="2"/>
<text x="72" y="252" font-size="64" font-weight="700">Find out how</text>
<text x="72" y="334" font-size="64" font-weight="700">software works.</text>
<text x="72" y="393" font-size="27" fill="#5b6570">Reverse engineering with your coding agent.</text>
<g stroke="#dce2e8" stroke-width="2">
<rect x="72" y="469" width="260" height="90" rx="10" fill="#ffffff"/>
<rect x="398" y="469" width="324" height="90" rx="10" fill="#f0f5fa" stroke="#294f82"/>
<rect x="788" y="469" width="340" height="90" rx="10" fill="#ffffff"/>
</g>
<g text-anchor="middle" font-size="27">
<text x="202" y="525">Your agent</text>
<text x="560" y="525" fill="#294f82" font-weight="700">REA + analysis tools</text>
<text x="958" y="525">Code &amp; evidence</text>
</g>
<g stroke="#5b6570" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path d="M350 514 H380 M372 506 L380 514 L372 522"/>
<path d="M740 514 H770 M762 506 L770 514 L762 522"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Dinosaur speed lab — REA</title>
<title>Chrome Dinosaur Game: Rebuild Its Speed Rule — REA</title>
<meta
name="description"
content="Read the dinosaur game’s speed rule with REA, rebuild it, then change the speed and play."
/>
<link rel="canonical" href="https://rea.tools/examples/dino-lab/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="Chrome Dinosaur Game: Rebuild Its Speed Rule — REA"
/>
<meta
property="og:description"
content="Read the dinosaur game’s speed rule with REA, rebuild it, then change the speed and play."
/>
<meta property="og:url" content="https://rea.tools/examples/dino-lab/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="Chrome Dinosaur Game: Rebuild Its Speed Rule — REA"
/>
<meta
name="twitter:description"
content="Read the dinosaur game’s speed rule with REA, rebuild it, then change the speed and play."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
@@ -2,6 +2,11 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="robots" content="noindex" />
<meta
name="description"
content="A small Electron Notes app for the REA JavaScript analysis guide."
/>
<title>Notes — Electron analysis example</title>
<script src="renderer.js" defer></script>
</head>
@@ -3,6 +3,11 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex" />
<meta
name="description"
content="A small Notes app for the REA browser and network analysis guide."
/>
<title>Notes — REA browser example</title>
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>FAQ — REA</title>
<title>REA FAQ: Setup, Updates and Troubleshooting</title>
<meta
name="description"
content="Answers to common REA questions: installation, updates, analysis tools, coding agents, troubleshooting and sharing examples."
/>
<link rel="canonical" href="https://rea.tools/faq/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="REA FAQ: Setup, Updates and Troubleshooting"
/>
<meta
property="og:description"
content="Answers to common REA questions: installation, updates, analysis tools, coding agents, troubleshooting and sharing examples."
/>
<meta property="og:url" content="https://rea.tools/faq/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="REA FAQ: Setup, Updates and Troubleshooting"
/>
<meta
name="twitter:description"
content="Answers to common REA questions: installation, updates, analysis tools, coding agents, troubleshooting and sharing examples."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Your first investigation — REA</title>
<title>Your First Reverse Engineering Investigation — REA</title>
<meta
name="description"
content="Try REA on a small Notes app. Set up your agent, ask where exported notes go, and check the CSV formatter with a sample title."
/>
<link rel="canonical" href="https://rea.tools/first-investigation/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="Your First Reverse Engineering Investigation — REA"
/>
<meta
property="og:description"
content="Try REA on a small Notes app. Set up your agent, ask where exported notes go, and check the CSV formatter with a sample title."
/>
<meta property="og:url" content="https://rea.tools/first-investigation/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="Your First Reverse Engineering Investigation — REA"
/>
<meta
name="twitter:description"
content="Try REA on a small Notes app. Set up your agent, ask where exported notes go, and check the CSV formatter with a sample title."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Get started — REA</title>
<title>Install REA and Set Up Your Coding Agent</title>
<meta
name="description"
content="Connect REA to your coding agent or inspect a local JavaScript or Electron app from the terminal. Setup, first questions and native provider guides."
/>
<link rel="canonical" href="https://rea.tools/get-started/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="Install REA and Set Up Your Coding Agent"
/>
<meta
property="og:description"
content="Connect REA to your coding agent or inspect a local JavaScript or Electron app from the terminal. Setup, first questions and native provider guides."
/>
<meta property="og:url" content="https://rea.tools/get-started/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="Install REA and Set Up Your Coding Agent"
/>
<meta
name="twitter:description"
content="Connect REA to your coding agent or inspect a local JavaScript or Electron app from the terminal. Setup, first questions and native provider guides."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../assets/styles.css" />
+35 -1
View File
@@ -3,11 +3,45 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Browser observation — REA</title>
<title>Browser and Network Analysis — REA</title>
<meta
name="description"
content="Observe a Notes export with REA: select a browser page, capture a click, inspect the request and follow the export script."
/>
<link rel="canonical" href="https://rea.tools/guides/browser/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta property="og:title" content="Browser and Network Analysis — REA" />
<meta
property="og:description"
content="Observe a Notes export with REA: select a browser page, capture a click, inspect the request and follow the export script."
/>
<meta property="og:url" content="https://rea.tools/guides/browser/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Browser and Network Analysis — REA" />
<meta
name="twitter:description"
content="Observe a Notes export with REA: select a browser page, capture a click, inspect the request and follow the export script."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+35 -1
View File
@@ -3,11 +3,45 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Guides — REA</title>
<title>Reverse Engineering Guides — REA</title>
<meta
name="description"
content="Learn REA through short worked examples: a native calculation, an Electron CSV export and a browser request."
/>
<link rel="canonical" href="https://rea.tools/guides/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta property="og:title" content="Reverse Engineering Guides — REA" />
<meta
property="og:description"
content="Learn REA through short worked examples: a native calculation, an Electron CSV export and a browser request."
/>
<meta property="og:url" content="https://rea.tools/guides/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Reverse Engineering Guides — REA" />
<meta
name="twitter:description"
content="Learn REA through short worked examples: a native calculation, an Electron CSV export and a browser request."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>JavaScript &amp; Electron — REA</title>
<title>JavaScript and Electron Analysis — REA</title>
<meta
name="description"
content="Trace a Notes CSV export through a renderer API, preload and IPC handler with REA. Includes downloadable example files and a verified analysis result."
/>
<link rel="canonical" href="https://rea.tools/guides/javascript/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="JavaScript and Electron Analysis — REA"
/>
<meta
property="og:description"
content="Trace a Notes CSV export through a renderer API, preload and IPC handler with REA. Includes downloadable example files and a verified analysis result."
/>
<meta property="og:url" content="https://rea.tools/guides/javascript/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="JavaScript and Electron Analysis — REA"
/>
<meta
name="twitter:description"
content="Trace a Notes CSV export through a renderer API, preload and IPC handler with REA. Includes downloadable example files and a verified analysis result."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+35 -1
View File
@@ -3,11 +3,45 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Native analysis — REA</title>
<title>Native Binary Analysis — REA</title>
<meta
name="description"
content="Recover a native calculation with REA: inspect instructions, follow a caller, read constants and check reconstructed C."
/>
<link rel="canonical" href="https://rea.tools/guides/native/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta property="og:title" content="Native Binary Analysis — REA" />
<meta
property="og:description"
content="Recover a native calculation with REA: inspect instructions, follow a caller, read constants and check reconstructed C."
/>
<meta property="og:url" content="https://rea.tools/guides/native/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Native Binary Analysis — REA" />
<meta
name="twitter:description"
content="Recover a native calculation with REA: inspect instructions, follow a caller, read constants and check reconstructed C."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+42 -2
View File
@@ -3,10 +3,50 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>REA — Reverse Engineer Anything</title>
<title>REA — Reverse Engineering for Your Coding Agent</title>
<meta
name="description"
content="Inspect apps, binaries and runtime behavior with your coding agent or terminal. REA connects local analysis tools through CLI and MCP."
content="Reverse engineer apps, binaries and browser behavior with your coding agent. Follow clear guides and try interactive examples with REA."
/>
<link rel="canonical" href="https://rea.tools/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="REA — Reverse Engineering for Your Coding Agent"
/>
<meta
property="og:description"
content="Reverse engineer apps, binaries and browser behavior with your coding agent. Follow clear guides and try interactive examples with REA."
/>
<meta property="og:url" content="https://rea.tools/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="REA — Reverse Engineering for Your Coding Agent"
/>
<meta
name="twitter:description"
content="Reverse engineer apps, binaries and browser behavior with your coding agent. Follow clear guides and try interactive examples with REA."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml" />
+4
View File
@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://rea.tools/sitemap.xml
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>How an authenticator generates a login code — REA</title>
<title>Aegis APK Reverse Engineering: Login Codes — REA</title>
<meta
name="description"
content="Inspect the Aegis Android APK with REA, recover its six-digit login-code calculation and try a reconstruction with an adjustable clock."
/>
<link rel="canonical" href="https://rea.tools/showcase/aegis/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="Aegis APK Reverse Engineering: Login Codes — REA"
/>
<meta
property="og:description"
content="Inspect the Aegis Android APK with REA, recover its six-digit login-code calculation and try a reconstruction with an adjustable clock."
/>
<meta property="og:url" content="https://rea.tools/showcase/aegis/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="Aegis APK Reverse Engineering: Login Codes — REA"
/>
<meta
name="twitter:description"
content="Inspect the Aegis Android APK with REA, recover its six-digit login-code calculation and try a reconstruction with an adjustable clock."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Solve a CTF flag checker — REA</title>
<title>CTF Reverse Engineering: DownUnderCTF Flag Checker — REA</title>
<meta
name="description"
content="Use REA to solve DownUnderCTF's masked squares flag checker. Find the checking functions, extract the masks and compare a recovered flag with the original program."
/>
<link rel="canonical" href="https://rea.tools/showcase/ctf/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="CTF Reverse Engineering: DownUnderCTF Flag Checker — REA"
/>
<meta
property="og:description"
content="Use REA to solve DownUnderCTF's masked squares flag checker. Find the checking functions, extract the masks and compare a recovered flag with the original program."
/>
<meta property="og:url" content="https://rea.tools/showcase/ctf/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="CTF Reverse Engineering: DownUnderCTF Flag Checker — REA"
/>
<meta
name="twitter:description"
content="Use REA to solve DownUnderCTF's masked squares flag checker. Find the checking functions, extract the masks and compare a recovered flag with the original program."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Rebuilding DX-Ball — REA</title>
<title>DX-Ball Reverse Engineering: Sound Panning — REA</title>
<meta
name="description"
content="Follow a DX-Ball reconstruction from binary analysis through REA to maintained C, original-x86 tests and matching compiled function bytes."
/>
<link rel="canonical" href="https://rea.tools/showcase/dx-ball/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="DX-Ball Reverse Engineering: Sound Panning — REA"
/>
<meta
property="og:description"
content="Follow a DX-Ball reconstruction from binary analysis through REA to maintained C, original-x86 tests and matching compiled function bytes."
/>
<meta property="og:url" content="https://rea.tools/showcase/dx-ball/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="DX-Ball Reverse Engineering: Sound Panning — REA"
/>
<meta
name="twitter:description"
content="Follow a DX-Ball reconstruction from binary analysis through REA to maintained C, original-x86 tests and matching compiled function bytes."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Case studies — REA</title>
<title>Reverse Engineering Case Studies — REA</title>
<meta
name="description"
content="Follow REA investigations through code, figures and demos: an Android authenticator, DX-Ball, Notion, TH04 and a DownUnderCTF flag checker."
/>
<link rel="canonical" href="https://rea.tools/showcase/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="Reverse Engineering Case Studies — REA"
/>
<meta
property="og:description"
content="Follow REA investigations through code, figures and demos: an Android authenticator, DX-Ball, Notion, TH04 and a DownUnderCTF flag checker."
/>
<meta property="og:url" content="https://rea.tools/showcase/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="Reverse Engineering Case Studies — REA"
/>
<meta
name="twitter:description"
content="Follow REA investigations through code, figures and demos: an Android authenticator, DX-Ball, Notion, TH04 and a DownUnderCTF flag checker."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Following Notion's clipboard through Electron — REA</title>
<title>Notion Electron Reverse Engineering: Clipboard — REA</title>
<meta
name="description"
content="Follow Notion's clipboard API through Electron's preload, IPC wrappers and main process, with short code excerpts and a look at its rich clipboard format."
/>
<link rel="canonical" href="https://rea.tools/showcase/notion/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="Notion Electron Reverse Engineering: Clipboard — REA"
/>
<meta
property="og:description"
content="Follow Notion's clipboard API through Electron's preload, IPC wrappers and main process, with short code excerpts and a look at its rich clipboard format."
/>
<meta property="og:url" content="https://rea.tools/showcase/notion/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="Notion Electron Reverse Engineering: Clipboard — REA"
/>
<meta
name="twitter:description"
content="Follow Notion's clipboard API through Electron's preload, IPC wrappers and main process, with short code excerpts and a look at its rich clipboard format."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+41 -1
View File
@@ -3,11 +3,51 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>TH04 bullet patterns — REA</title>
<title>TH04 Reverse Engineering: DOS Bullet Patterns — REA</title>
<meta
name="description"
content="Inspect TH04's original 16-bit DOS code with REA. Follow the assembly and recovered C++ to see how a ring of bullets is spaced and aimed at the player."
/>
<link rel="canonical" href="https://rea.tools/showcase/th04/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="REA" />
<meta
property="og:title"
content="TH04 Reverse Engineering: DOS Bullet Patterns — REA"
/>
<meta
property="og:description"
content="Inspect TH04's original 16-bit DOS code with REA. Follow the assembly and recovered C++ to see how a ring of bullets is spaced and aimed at the player."
/>
<meta property="og:url" content="https://rea.tools/showcase/th04/" />
<meta
property="og:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta
property="og:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content="TH04 Reverse Engineering: DOS Bullet Patterns — REA"
/>
<meta
name="twitter:description"
content="Inspect TH04's original 16-bit DOS code with REA. Follow the assembly and recovered C++ to see how a ring of bullets is spaced and aimed at the player."
/>
<meta
name="twitter:image"
content="https://rea.tools/assets/social-card.png"
/>
<meta
name="twitter:image:alt"
content="REA — Find out how software works. Reverse engineering with your coding agent."
/>
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
<link rel="stylesheet" href="../../assets/styles.css" />
+2
View File
@@ -0,0 +1,2 @@
# SVG-to-PNG conversion for the generated social preview.
CairoSVG==2.9.1
+5
View File
@@ -20,3 +20,8 @@ name = "rea-website"
workers_dev = true
preview_urls = false
routes = [{ pattern = "rea.tools", custom_domain = true }]
# Production receives the already prepared and verified shared Pages artifact.
# Override the preview build so deployment does not regenerate local assets.
[env.production.build]
command = ""