Skip to content

Latest commit

 

History

History
524 lines (414 loc) · 20.9 KB

File metadata and controls

524 lines (414 loc) · 20.9 KB

OpenEggbert.com — Refactoring Plan


Part 2 — Markdown Source + HTML Generation Analysis

Goal

Write all page content as Markdown files and use a build script to generate the same HTML+CSS output that currently exists, without changing the visual appearance.


2.1 Complete Inventory of HTML Constructs in <main>

The following table covers all HTML constructs found across the 125 article pages.

Standard constructs — map directly to Markdown

HTML construct Count of pages Markdown equivalent
<h1><h4> all pages # … ####
<p> ~70 pages blank line between paragraphs
<br> (line break) very common two trailing spaces or \ at end of line (CommonMark)
<ul> / <ol> / <li> ~110 pages - item / 1. item
<b> / <strong> ~90 pages **text**
<i> / <em> ~20 pages *text*
<s> strikethrough 2 pages ~~text~~ (GFM extension)
<a href="…"> plain links all pages [label](href)
<code> inline ~15 pages `code`
<pre><code> block 14 pages ```lang … ``` fenced block
<blockquote> 5 pages > text
<hr> 4 pages ---
<img src alt width> ~20 pages ![alt](src)

Constructs needing custom handling

HTML construct Pages Problem Solution
<table class="infobox"> 14 Floating right, complex colspan, links inside cells Raw HTML block in Markdown; or YAML front-matter shorthand
<table> with rowspan/colspan ~12 Markdown tables do not support merged cells Keep as raw HTML block in Markdown
<table> simple (no merge) ~8 Supported Markdown pipe table syntax
<a class="ref"> 6 pages Custom CSS class on link [text](url){.ref} using attr_list extension, or raw HTML
<ul id="tags"> at page bottom 11 Semantic tag list YAML front-matter tags: list; template renders it
<figure> + <figcaption> 3 No native Markdown equivalent Raw HTML block, or ![alt](src "caption") + custom CSS
<div style="background: orange;"> 1 Warning/callout box > [!WARNING] callout syntax (GitHub-flavored) or custom class via attr_list
style="background:#bbbbbb" on <b> 4 occurrences Inline highlighted term Raw HTML <b style="…"> inside Markdown
style="max-width:300px;" on <img> 17 occurrences Image size constraint ![alt](src){style="max-width:300px;"} with attr_list, or raw <img>
style="display:block" on <a> 51 occurrences All in one large table page (Blupi_websites) Keep that page as raw HTML; or add .block CSS class
<main style="background: #f5b7b1;"> 1 (error page) Special background on error page Front-matter body_class: error and template adds inline style

Constructs that disappear (handled by template / JS)

HTML construct Reason disappears
<header> + <nav> Already injected by buildPage()
<footer> Already injected by buildPage()
<div id="tocButton"> + <div id="toc"> Already generated by loadContent() — the template adds them
<body onload=…> Template uses DOMContentLoaded
<script>PAGE_CONFIG={…}</script> Template generates this from front-matter
<meta description/keywords/author> Already injected by buildPage()
<base href="…"> Template calculates depth from file path
<section> wrapper Template wraps content in <section> automatically

2.2 Proposed Markdown File Format

Every article becomes one .md file with a YAML front-matter header. The front-matter replaces PAGE_CONFIG and provides metadata. The rest is standard Markdown.

---
title: "Speedy Blupi (Windows)"
breadcrumb:
  - {label: "Blupi",  href: "Blupi/index.html"}
  - {label: "Games",  href: "Blupi/Games/index.html"}
  - {label: "Speedy Blupi (Windows)", href: "Blupi/Games/Speedy_Blupi_(Windows)/index.html"}
subpages:
  - {label: "Go Up",    href: "Blupi/Games/index.html"}
  - {label: "Blocks",   href: "Blupi/Games/Speedy_Blupi_(Windows)/Blocks/index.html"}
  - {label: "Levels",   href: "Blupi/Games/Speedy_Blupi_(Windows)/Levels/index.html"}
tags:
  - "Games created by Daniel Roux"
---

<!-- Raw HTML infobox (kept as-is) -->
<table class="infobox"> … </table>

## Introduction

Speedy Blupi is a 2D [platformer](Article_does_not_yet_exist_or_link_is_broken/index.html) game
originally developed by Swiss company [Epsitec](Blupi/Epsitec/index.html).

## Minimum system requirements

- PC Windows 95/98/98SE/Me/2000/XP/Vista/7/8/10/11
- [Pentium 100](Technologies/Programming_languages/Assembly_Language/I586/Pentium_100/index.html) MHz CPU
- 16 MB RAM

## Code example

```java
float velocityY = 0;
final float gravity = -0.5f;
Header A Header B
value 1 value 2
NameEffects

External source{.ref}


---

### 2.3 Recommended Build Tool: Python + `python-markdown`

**Why Python:**
- Already used in this project (the transformation script is Python)
- No new runtime to install
- `python-markdown` library with extensions handles 90 % of constructs natively

**Extensions needed from `python-markdown`:**

| Extension | Purpose |
|---|---|
| `tables` | Basic pipe-table syntax |
| `fenced_code` | ```` ```lang … ``` ```` code blocks |
| `attr_list` | Add `{.classname}` / `{style="…"}` to any element |
| `toc` | Auto-generate heading ids (already done by JS, but useful for anchors) |
| `nl2br` | Convert single newlines to `<br>` (matches current heavy `<br>` usage) |
| `sane_lists` | Correct list nesting |
| `meta` | Read YAML-style front-matter (or use PyYAML separately) |
| `md_in_html` | Allow Markdown inside raw HTML blocks |

**Alternative: Pandoc (single binary)**

Pandoc can convert Markdown → HTML with a custom `--template` flag. It supports all the above natively plus finer control over raw HTML passthrough. The downside is it requires installing the Pandoc binary (~30 MB), which is non-trivial in CI without caching.

---

### 2.4 Build Script Architecture

src/ About/ index.md ← Markdown content + front-matter Abbreviations/ index.md Blupi/ index.md Games/ Speedy_Blupi_(Windows)/ index.md Levels/ I/ 060/ index.md … _template.html ← HTML shell (head + script refs, buildPage calls)

build.py ← reads src//*.md, writes root//*.html styles.css ← unchanged script.js ← unchanged (buildPage + loadContent)


**`build.py` logic (per file):**

1. Read `src/.../index.md`
2. Parse YAML front-matter (title, breadcrumb, subpages, tags, optional depth)
3. Convert Markdown body → HTML fragment using `python-markdown`
4. Calculate `<base href>` from file depth
5. Generate `<script>PAGE_CONFIG={…}</script>` from front-matter
6. Generate `<ul id="tags">` from `tags:` list (if present)
7. Write the full HTML to the matching path under the root (e.g. `About/index.html`)

The output HTML files are byte-for-byte compatible with the current format — the same `script.js` and `styles.css` work unchanged.

---

### 2.5 What Stays as Raw HTML in Markdown

These constructs are kept as raw HTML blocks directly inside the `.md` files and passed through unchanged by the Markdown processor:

1. **`<table class="infobox">`** — all 14 occurrences; too complex and unique to convert
2. **Tables with `rowspan`/`colspan`** — ~12 pages (Cheats table, Level tables, Blupi_websites)
3. **`<figure><img …><figcaption>`** — 3 pages
4. **`<div style="background: orange;">` callout boxes** — 1 page
5. **Bold text with `style="background:#bbbbbb"`** — 4 occurrences (could add a `.highlight` CSS class instead)

Estimated **raw HTML percentage: ~10–15 %** of content volume across the site. The remaining 85–90 % converts cleanly to readable Markdown.

---

### 2.6 Migration Strategy (no changes to existing pages during migration)

1. **Phase 1 (setup):** Create `src/` directory and `build.py`. Write the HTML template. Set up GitHub Actions to run `build.py` and commit the generated HTML back to the repo on every push to `src/`.
2. **Phase 2 (pilot):** Convert 5–10 small pages from HTML → Markdown, verify output is visually identical.
3. **Phase 3 (bulk conversion):** Use a Python script to auto-convert the existing `index.html` files to `index.md` (reverse of `build.py`). Auto-conversion handles ~85 % of pages. Manually fix the remaining 15 % with complex tables/infoboxes.
4. **Phase 4 (switch):** Once all Markdown sources are verified, the generated HTML files are the only files committed to the root. Source `.md` files live in `src/`.

**CI/CD pipeline (GitHub Actions):**

```yaml
name: Build site
on:
  push:
    paths: ['src/**', 'build.py', '_template.html']
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install markdown PyYAML
      - run: python build.py
      - uses: actions/upload-pages-artifact@v3
        with: {path: '.'}

Or simpler: commit generated HTML directly to the repo (no separate artifact step), which keeps GitHub Pages working without changing the deployment method.


2.7 Pros and Cons Summary

Aspect Current (raw HTML) After Markdown migration
Writing a new article ~15-line HTML shell + content HTML Pure Markdown text, no HTML knowledge needed
Infoboxes Write table HTML by hand Still write table HTML (raw block)
Simple tables HTML table markup Markdown pipe syntax
Links <a href="…">label</a> [label](href)
Build step required No Yes (Python script + GitHub Actions)
Previewing in editor Browser only Any Markdown editor (VS Code, Obsidian, etc.)
Raw HTML still needed Everywhere ~10–15 % of content (complex tables, infoboxes)
Risk Low (already done) Medium (new build pipeline to maintain)

1. Current State Analysis

What the site is

A static HTML fan-community website for the Speedy Blupi / Open Eggbert game series, hosted (likely via GitHub Pages) without any build toolchain. There are no frameworks, no bundlers — just raw HTML files, one shared styles.css, and one shared script.js, all residing at the repository root.

Size

  • 127 HTML files spread across approximately 129 directories
  • Maximum directory depth: 6 levels (e.g. Blupi/Games/Speedy_Blupi_(Windows)/Levels/I/060/index.html)
  • One Template/index.html used as a copy-paste starting point for new pages
  • One special tree.html at root (full article tree, no shared boilerplate — standalone page)

URL / directory convention

Every article lives at <Category>/<Subcategory>/.../index.html. All asset references (CSS, JS, favicon) are resolved via <base href="…"> pointing back to the site root, so all links inside a page are written as absolute-from-root paths regardless of how deep the file sits.


2. Identified Duplications

Every single index.html (except tree.html) contains the following identical blocks:

2a. <head> block (~16 lines, mostly static)

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Open Eggbert, a free and open-source game…"/>
<meta name="keywords" content="Blupi, Speedy Blupi, Speedy Eggbert, …"/>
<meta name="author" content="Robert Vokáč">
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="favicon.ico" type="image/x-icon" sizes="32x32">
<script type="text/javascript" src="script.js"></script>

Only two things differ: <title> text and <base href="…"> depth.

2b. <header> + <nav> block (~12 lines, 100% identical)

<header>
  <div id="main_banner"><a href="index.html">Open Eggbert</a></div>
  <nav>
    <ul>
      <li><a href="index.html">Home</a></li>
      <li><a href="About/index.html">About</a></li>
      <li><a href="Blupi/index.html">Blupi</a></li>
      <li><a href="Projects/index.html">Projects</a></li>
      <li><a href="Technologies/index.html">Technologies</a></li>
    </ul>
  </nav>
</header>

Repeated in all 126 article pages.

2c. <footer> block (~5 lines, 100% identical)

<footer>
  <p>Content is available under <a href="https://creativecommons.org/licenses/by-sa/4.0/" >
    Creative Commons Attribution-ShareAlike 4.0 International License</a> unless otherwise noted.</p>
</footer>

Repeated in all 126 article pages.

2d. <body onload="loadContent()"> + TOC placeholders

<body onload="loadContent()"><div id="tocButton"></div>
<div id="toc"></div>

The TOC div pair appears inside <main> on every article page.

2e. #breadcrumb_hierarchy_panel structure

The outer HTML structure is always the same:

<div id="breadcrumb_hierarchy_panel">
  <div id="breadcrumb"></div>
  <div id="hierarchy_panel"></div>
</div>

Only the inner links differ (breadcrumb chain and list of child pages).

Summary of per-file uniqueness

Element Unique per page?
<title> Yes
<base href> Yes (depth varies)
<header> / <nav> No — identical everywhere
<footer> No — identical everywhere
Breadcrumb links Yes
Hierarchy panel links Yes
<main> content Yes — all article text

3. Proposed Refactoring Approach

Strategy: JavaScript-injected shell + data-driven navigation

No build step is introduced. All pages remain plain .html files. The shared boilerplate (header, nav, footer) is removed from every page and instead injected by script.js at page-load time.

Each article page is reduced to a minimal shell:

DOCTYPE + <html>
  <head>               ← only title, base href, CSS/JS refs
  <body>               ← NO header/footer markup
    <script>           ← small inline config object (breadcrumb + subpages)
    PAGE_CONFIG = { … };
    </script>
    <main>
      <section>
        <h1>…</h1>
        <!-- article content only -->
      </section>
    </main>

script.js is extended to:

  1. Read PAGE_CONFIG before injecting.
  2. document.body.insertAdjacentHTML('afterbegin', headerHTML) — inject <header> + <nav> at top.
  3. Build #breadcrumb_hierarchy_panel from PAGE_CONFIG.breadcrumb and PAGE_CONFIG.subpages.
  4. document.body.insertAdjacentHTML('beforeend', footerHTML) — inject <footer>.
  5. Run the existing TOC logic (already in loadContent()).

What the minimal page shell looks like (new Template)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Open Eggbert - ARTICLE_TITLE</title>
  <base href="RELATIVE_PATH_TO_ROOT" target="_self">
  <link rel="stylesheet" href="styles.css">
  <link rel="icon" href="favicon.ico" type="image/x-icon" sizes="32x32">
  <script src="script.js"></script>
</head>
<body>
<script>
PAGE_CONFIG = {
  title: "ARTICLE_TITLE",
  breadcrumb: [
    { label: "ParentA", href: "ParentA/index.html" },
    { label: "ParentB", href: "ParentA/ParentB/index.html" }
  ],
  subpages: [
    { label: "Go Up",   href: "ParentA/ParentB/index.html" },
    { label: "Child 1", href: "ParentA/ParentB/ARTICLE_TITLE/Child1/index.html" },
    { label: "Child 2", href: "ParentA/ParentB/ARTICLE_TITLE/Child2/index.html" }
  ]
};
</script>

<main>
  <section>
    <h1>ARTICLE_TITLE</h1>
    <div id="tocButton"></div>
    <div id="toc"></div>

    <!-- article content here -->

  </section>
</main>
</body>
</html>

Lines saved per page: approximately 35–40 lines of boilerplate → down to ~10 lines of shell.

Changes to script.js

Add a buildPage() function that runs before loadContent():

const NAV_LINKS = [
  { label: "Home",         href: "index.html" },
  { label: "About",        href: "About/index.html" },
  { label: "Blupi",        href: "Blupi/index.html" },
  { label: "Projects",     href: "Projects/index.html" },
  { label: "Technologies", href: "Technologies/index.html" }
];

const FOOTER_HTML =
  `<footer><p>Content is available under ` +
  `<a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank" rel="noopener noreferrer">` +
  `Creative Commons Attribution-ShareAlike 4.0 International License</a> unless otherwise noted.</p></footer>`;

function buildPage() {
  // Build <header>
  const navItems = NAV_LINKS.map(n => `<li><a href="${n.href}">${n.label}</a></li>`).join('');
  const headerHTML =
    `<header>` +
    `<div id="main_banner"><a href="index.html">Open Eggbert</a></div>` +
    `<nav><ul>${navItems}</ul></nav>` +
    `</header>`;
  document.body.insertAdjacentHTML('afterbegin', headerHTML);

  // Build breadcrumb + hierarchy panel from PAGE_CONFIG
  if (typeof PAGE_CONFIG !== 'undefined') {
    const breadcrumbLinks = (PAGE_CONFIG.breadcrumb || [])
      .map(b => `<a href="${b.href}">${b.label}</a>`)
      .join(' / ');
    const subpageLinks = (PAGE_CONFIG.subpages || [])
      .map(s => `<a href="${s.href}">${s.label}</a>`)
      .join('');
    const panelHTML =
      `<div id="breadcrumb_hierarchy_panel">` +
      `<div id="breadcrumb">${breadcrumbLinks}</div>` +
      `<div id="hierarchy_panel">${subpageLinks}</div>` +
      `</div>`;
    document.querySelector('main').insertAdjacentHTML('beforebegin', panelHTML);
  }

  // Inject <footer>
  document.body.insertAdjacentHTML('beforeend', FOOTER_HTML);
}

<body> changes from <body onload="loadContent()"> to just <body>, and both buildPage() and loadContent() are called via:

document.addEventListener('DOMContentLoaded', () => { buildPage(); loadContent(); });

Meta tags: shared vs. per-page

The current <meta name="description"> and <meta name="keywords"> tags are identical on every page (they always describe the site, not the article). Two options:

  • Option A (simplest): Move these into script.js / buildPage() and inject them into <head> dynamically — they disappear from every page file.
  • Option B (per-page SEO): Keep them in the page <head> so each page can have a unique description in the future.

Recommendation: Option A for now (they are already identical everywhere, so no per-page value is lost), but make PAGE_CONFIG.description an optional override that, if present, replaces the default.


4. Benefits

Benefit Detail
Adding a new page Copy minimal template (~25 lines), fill PAGE_CONFIG + <main> content. No boilerplate copying.
Updating nav Change NAV_LINKS in script.js — propagates to all 127 pages instantly.
Updating footer license Change FOOTER_HTML in script.js — done. Currently requires editing 127 files.
Updating meta description Same — one place instead of 127.
Fewer merge conflicts Articles only contain content, not structural HTML.
No build toolchain needed Pure browser-side JS; works with GitHub Pages as-is.

5. What Does NOT Change

  • The URL structure (Category/Subcategory/index.html) stays identical.
  • The styles.css is untouched.
  • The <base href> trick for depth-relative asset loading is kept.
  • The visual appearance is pixel-identical to the current site (same CSS, same HTML output after JS runs).
  • The tree.html page is standalone and needs no changes (it already has its own inline styles).
  • SEO: Google and most modern crawlers execute JavaScript, so injected content is indexed. For the rare crawler that does not execute JS, the page title and article <h1> content are still in the static HTML.

6. Migration Strategy

Migration can be done in small, safe batches:

  1. Update script.js — add buildPage() and change the init call. No page breaks yet (existing pages still have their own header/footer).
  2. Convert pages one section at a time — start with a low-traffic section (e.g. About/), verify visually, then proceed.
  3. Remove old boilerplate from converted pages only after verifying the injected version looks correct.
  4. Update Template/index.html to the new minimal format so all future pages start slim.
  5. Remove <meta name="description/keywords/author"> from all pages once buildPage() handles them.

A simple shell script can verify each converted page still contains the expected PAGE_CONFIG variable and <main> tag.


7. Optional Future Improvement: Markdown content

Once the JS injection is in place, a further step (not required) would be to write article content in Markdown and convert it with a minimal build script (e.g. pandoc or a Node.js script) that:

  • Reads each content.md file
  • Wraps it in the minimal HTML shell with appropriate PAGE_CONFIG
  • Outputs index.html

This would allow article authors to write plain Markdown without touching any HTML at all. This step is entirely optional and should only be considered after the JS injection refactor is complete and working.