diff --git a/.github/workflows/main-branch.yml b/.github/workflows/main-branch.yml
index ff5d50a..570338f 100644
--- a/.github/workflows/main-branch.yml
+++ b/.github/workflows/main-branch.yml
@@ -30,37 +30,7 @@ jobs:
pip install -r requirements.txt
- name: Build static site bundle
- run: |
- python - <<'PY'
- import json
- import shutil
- import time
- from pathlib import Path
-
- start = time.perf_counter()
- root = Path('.')
- dist = Path('dist')
- if dist.exists():
- shutil.rmtree(dist)
- dist.mkdir(parents=True, exist_ok=True)
-
- for asset in ['index.html', 'style.css', 'app.js', 'ai-instruct.txt']:
- shutil.copy2(root / asset, dist / asset)
-
- duration = time.perf_counter() - start
- report_dir = Path('ci_reports')
- report_dir.mkdir(parents=True, exist_ok=True)
- (report_dir / 'build_status.json').write_text(
- json.dumps(
- {
- 'status': 'succeeded',
- 'artifact': 'github-pages',
- 'duration': duration,
- },
- indent=2,
- )
- )
- PY
+ run: python scripts/build_static.py
- name: Upload static artifact
uses: actions/upload-pages-artifact@v3
diff --git a/README.md b/README.md
index 33970bd..f644f42 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,10 @@ Two separate GitHub Actions workflows keep deployments fast and informative:
- **Main Branch Delivery** (`.github/workflows/main-branch.yml`)
- Triggers on pushes to `main` and manual dispatches.
- - Builds the static bundle, uploads the GitHub Pages artifact, and records a
- machine-readable build summary.
+ - Runs `scripts/build_static.py`, which inlines the CSS and JavaScript so the
+ GitHub Pages artifact is a self-contained `index.html` (eliminating missing
+ asset issues on the published site).
+ - Uploads the bundle artifact and records a machine-readable build summary.
- Executes the same test suite and reports results without blocking deploys.
- Deploys successful builds to GitHub Pages.
diff --git a/index.html b/index.html
index d7fa67b..fb25e4d 100644
--- a/index.html
+++ b/index.html
@@ -4,8 +4,12 @@
Unity Voice Chat
+
+
+
+
diff --git a/scripts/build_static.py b/scripts/build_static.py
new file mode 100644
index 0000000..c9275f8
--- /dev/null
+++ b/scripts/build_static.py
@@ -0,0 +1,88 @@
+"""Build the GitHub Pages bundle with inlined assets."""
+from __future__ import annotations
+
+import json
+import shutil
+import time
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[1]
+DIST = ROOT / "dist"
+CI_REPORTS = ROOT / "ci_reports"
+
+CSS_START = ""
+CSS_END = ""
+JS_START = ""
+JS_END = ""
+
+
+def replace_block(source: str, start_marker: str, end_marker: str, replacement: str) -> str:
+ """Replace the block delimited by the provided markers.
+
+ Raises a ValueError when the markers are missing so the workflow fails fast
+ instead of silently producing a broken bundle.
+ """
+
+ try:
+ start_index = source.index(start_marker) + len(start_marker)
+ end_index = source.index(end_marker)
+ except ValueError as exc: # pragma: no cover - defensive guard
+ raise ValueError(
+ "Build markers missing in index.html. Ensure the start and end "
+ f"markers {start_marker!r} and {end_marker!r} exist."
+ ) from exc
+
+ before = source[:start_index]
+ after = source[end_index:]
+ return f"{before}\n{replacement}\n{after}"
+
+
+def build() -> None:
+ start = time.perf_counter()
+
+ if DIST.exists():
+ shutil.rmtree(DIST)
+ DIST.mkdir(parents=True, exist_ok=True)
+
+ index_html = (ROOT / "index.html").read_text(encoding="utf-8")
+ css = (ROOT / "style.css").read_text(encoding="utf-8")
+ javascript = (ROOT / "app.js").read_text(encoding="utf-8")
+
+ inlined_index = replace_block(
+ index_html,
+ CSS_START,
+ CSS_END,
+ f"",
+ )
+ inlined_index = replace_block(
+ inlined_index,
+ JS_START,
+ JS_END,
+ f"",
+ )
+
+ (DIST / "index.html").write_text(inlined_index, encoding="utf-8")
+
+ # Keep the original assets alongside the inlined bundle for local debugging
+ # and to avoid breaking any bookmarked resources.
+ shutil.copy2(ROOT / "style.css", DIST / "style.css")
+ shutil.copy2(ROOT / "app.js", DIST / "app.js")
+ shutil.copy2(ROOT / "ai-instruct.txt", DIST / "ai-instruct.txt")
+
+ duration = time.perf_counter() - start
+ CI_REPORTS.mkdir(parents=True, exist_ok=True)
+ (CI_REPORTS / "build_status.json").write_text(
+ json.dumps(
+ {
+ "status": "succeeded",
+ "artifact": "github-pages",
+ "duration": duration,
+ },
+ indent=2,
+ ),
+ encoding="utf-8",
+ )
+
+
+if __name__ == "__main__":
+ build()