Initial scaffold: ArchyHCL, a community hardware compatibility list for Archipelago
Build and validate / build (push) Successful in 1m38s

Modeled on three researched precedents (see README): OpenWrt's Table of
Hardware for the browsable sortable/filterable table UX, postmarketOS's
working/community/testing tiers for the status field (collapsed to
working/partial/broken here), and RaspiBlitz's scattered GitHub-issues
approach as a negative example to avoid — hence structured YAML report
files validated against a JSON Schema instead of free-text issue threads.

- data/reports/*.yml + data/schema.json: one file per report, schema
  requires `issues` whenever status is partial/broken
- scripts/build.py: validates every report and builds site/data.json,
  fails loudly on bad data (same idea as archy's own
  validate-app-manifest.sh)
- site/: plain HTML/CSS/JS, no framework or build step, fetches data.json
  client-side — search, filter by status/form-factor, sortable columns,
  click a row for issues/notes detail
- .github/ISSUE_TEMPLATE/hardware-report.yml: structured submission path
  for contributors who don't want to touch git directly
- .gitea/workflows/ci.yml: runs the build/validate step on push and PRs

Not yet deployed anywhere — see README's Deployment section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 11:32:58 +00:00
co-authored by Claude Sonnet 5
commit a22a9c9826
14 changed files with 855 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
name: Build and validate
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install pyyaml jsonschema
- name: Build and validate every report
run: python3 scripts/build.py
+116
View File
@@ -0,0 +1,116 @@
name: Hardware report
description: Report a device you've installed Archipelago on, working or not.
title: "hw: <device model>"
labels: ["hardware-report"]
body:
- type: markdown
attributes:
value: |
Thanks for reporting! A maintainer turns this into a data file under
`data/reports/` and it shows up on the site. If you're comfortable
with git, opening a PR directly is faster — see CONTRIBUTING.md.
- type: input
id: device_model
attributes:
label: Device model
placeholder: "Lenovo ThinkPad T430"
validations:
required: true
- type: dropdown
id: form_factor
attributes:
label: Form factor
options:
- laptop
- desktop
- mini-pc
- sbc
- server
- other
validations:
required: true
- type: input
id: cpu
attributes:
label: CPU
placeholder: "Intel Core i5-3320M"
validations:
required: true
- type: input
id: ram_gb
attributes:
label: RAM (GB)
placeholder: "8"
validations:
required: true
- type: input
id: storage
attributes:
label: Storage (type and size)
placeholder: "256GB SSD"
validations:
required: true
- type: input
id: wifi_chip
attributes:
label: WiFi chip
description: Exact chipset if you know it. Put "none" if wired-only.
placeholder: "Intel Centrino Advanced-N 6205"
validations:
required: true
- type: input
id: ethernet
attributes:
label: Ethernet chip (optional)
placeholder: "Intel 82579LM"
- type: input
id: archy_version
attributes:
label: Archipelago version tested
placeholder: "1.8.4-alpha"
validations:
required: true
- type: dropdown
id: install_method
attributes:
label: Install method
options:
- usb-iso
- netboot
- existing-os-script
- other
validations:
required: true
- type: dropdown
id: status
attributes:
label: Status
description: "working = no known issues. partial = runs with specific known issues (describe below). broken = does not install or run."
options:
- working
- partial
- broken
validations:
required: true
- type: input
id: tested_date
attributes:
label: Date tested
placeholder: "2026-08-21"
validations:
required: true
- type: textarea
id: issues
attributes:
label: Issues (required if status is partial or broken)
description: What broke, and any workaround you found.
- type: textarea
id: notes
attributes:
label: Other notes
description: BIOS/UEFI settings needed, quirks, links to a fuller writeup.
- type: input
id: submitted_by
attributes:
label: Attribution (optional)
description: Your npub, Gitea username, or leave blank to stay anonymous.
+3
View File
@@ -0,0 +1,3 @@
__pycache__/
*.pyc
.DS_Store
+50
View File
@@ -0,0 +1,50 @@
# Contributing to ArchyHCL
Two ways to add a report, pick whichever's easier for you.
## Option A — open an issue (no git needed)
[Open a hardware report issue](https://source.archipelago-foundation.org/lfg2025/ArchyHCL/issues/new?template=hardware-report.yml)
and fill in the form. A maintainer will turn it into a data file and it'll
show up on the site.
## Option B — open a PR directly
1. Copy `data/reports/example-thinkpad-t430.yml` to a new file named
`<device-slug>-<short-id>.yml` (e.g. `thinkpad-t430-a1b2.yml` — the
suffix just needs to make the filename unique if the same model's been
reported before).
2. Fill in your report. Every field is described in
[`data/schema.json`](data/schema.json); `issues` is required if your
`status` is `partial` or `broken`.
3. Validate locally before opening the PR:
```bash
pip install pyyaml jsonschema # if you don't have them
python3 scripts/build.py
```
This fails loudly (and tells you exactly which field) if anything's
wrong — same idea as [archy](https://source.archipelago-foundation.org/lfg2025/archy)'s
own `scripts/validate-app-manifest.sh`.
4. Open the PR. Once merged, `scripts/build.py` regenerates `site/data.json`
and the site picks it up.
## Updating an existing report
Devices change over time (firmware updates fix WiFi issues, etc.) — if
you're re-testing a device that's already listed, open a PR editing the
existing file rather than adding a duplicate. Keep the old `tested_date`
context in mind: bump it to your test date so readers know how fresh the
report is.
## What makes a good report
- Be exact about the WiFi chip if you can (`iwconfig`/`lspci` on Linux,
Device Manager on Windows if you dual-booted to check). "Realtek" alone
isn't as useful as "Realtek RTL8821CE" — chip-specific driver issues are
the single most common thing this list exists to surface.
- If `status` is `partial` or `broken`, describe *what* broke and *how you
noticed* (crash on boot? WiFi drops under load? specific app won't
start?) — "doesn't work" isn't actionable for the next person.
- If you found a workaround, put it in `notes` even if the underlying issue
isn't fixed. A working-with-a-workaround report is more useful than no
report at all.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 the Archipelago Project contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+84
View File
@@ -0,0 +1,84 @@
# ArchyHCL
A community hardware compatibility list for [Archipelago](https://source.archipelago-foundation.org/lfg2025/archy) —
what physical devices people have actually installed it on, what worked,
what didn't, and why. Model, CPU, RAM, storage, and WiFi chip for every
report, since WiFi chipsets are consistently the thing most likely to bite
someone doing an install on repurposed hardware.
**[Browse the list →](https://hcl.archipelago-foundation.org)** (once deployed —
see [Deployment](#deployment) below)
## Why this exists
Archipelago runs on whatever hardware people have lying around — old
laptops, mini PCs, SBCs — and that's a much wider, messier compatibility
surface than a project that only targets its own reference hardware. Before
building this, I looked at how a few existing communities solve the same
problem:
- **[OpenWrt's Table of Hardware](https://toh.openwrt.org/)** — a
searchable table of routers, generated nightly from structured wiki
pages into a `toh.json` file the site reads. Good model for the
browsing UX (sortable/filterable table of CPU/RAM/flash/WiFi chip), but
the underlying wiki pages are free-text and only loosely structured,
which the maintainers themselves have called out as a maintenance
problem over the years.
- **[postmarketOS's device list](https://wiki.postmarketos.org/wiki/Devices)** —
tiers devices into `main` / `community` / `testing` support levels, each
with a per-device page describing exactly what works and what doesn't.
Good model for the *status* field: a single working/broken flag loses
the "boots fine but WiFi is flaky" middle ground that's actually the
most common and most useful case to document.
- **[RaspiBlitz](https://github.com/raspiblitz/raspiblitz)** (closest
project to Archipelago in spirit — a Bitcoin/Lightning node OS for
repurposed hardware) — hardware feedback mostly lives scattered across
GitHub issues with no consistent fields, which makes it hard to search
or compare reports. Useful as a negative example: this is what we're
trying to avoid by using structured data files instead of free-form
issue threads.
**The model this repo uses**: one small YAML file per report
(`data/reports/*.yml`), validated against a JSON Schema, built into a
single `data.json` a plain static site reads — OpenWrt's browsable-table UX,
postmarketOS's tiered status field, and RaspiBlitz's actual subject matter
(node-OS-on-repurposed-hardware), on top of git-native, PR-reviewable data
instead of a wiki or a scattered issue tracker.
## Repo layout
```
data/
schema.json — the report schema (source of truth for what fields exist)
reports/*.yml — one file per hardware report
scripts/
build.py — validates every report and builds site/data.json
site/
index.html/.css/.js — the static site (no framework, no build step — just fetches data.json)
.gitea/issue_template/hardware-report.yml — structured issue form for reporting without git
```
## Contributing a report
See [CONTRIBUTING.md](CONTRIBUTING.md) — either open an issue with the
structured form, or add a YAML file directly and open a PR.
## Building the site locally
```bash
pip install pyyaml jsonschema
python3 scripts/build.py # writes site/data.json
python3 -m http.server 8000 --directory site
```
## Deployment
Not yet deployed. This is a static site (`site/`) with no backend — any
static host works (the same nginx-in-front-of-a-container pattern archy's
other apps already use, GitHub/Gitea Pages, or a plain S3-style bucket).
`scripts/build.py` should run in CI on every push to `main` so `data.json`
never drifts from the source reports; wiring that up is the next step.
## License
MIT — see [LICENSE](LICENSE).
+22
View File
@@ -0,0 +1,22 @@
device_model: "Raspberry Pi 5 (8GB)"
form_factor: sbc
cpu: "Broadcom BCM2712 (4x Cortex-A76)"
ram_gb: 8
storage:
type: nvme
size_gb: 512
wifi_chip: "Broadcom BCM43455 (onboard)"
ethernet: "Broadcom onboard Gigabit"
archy_version: "1.8.4-alpha"
install_method: netboot
status: partial
tested_date: "2026-08-10"
submitted_by: "example-contributor"
issues: |
Example report — replace with a real one. Onboard WiFi drops the
connection under sustained heavy P2P traffic (Bitcoin IBD); a workaround
is to run over Ethernet instead. Everything else (Lightning, apps,
dashboard) works fine.
notes: |
NVMe via the official PCIe HAT. Boots noticeably faster than SD-card
installs.
+18
View File
@@ -0,0 +1,18 @@
device_model: "Lenovo ThinkPad T430"
form_factor: laptop
cpu: "Intel Core i5-3320M"
ram_gb: 8
storage:
type: ssd
size_gb: 256
wifi_chip: "Intel Centrino Advanced-N 6205"
ethernet: "Intel 82579LM"
archy_version: "1.8.4-alpha"
install_method: usb-iso
status: working
tested_date: "2026-08-15"
submitted_by: "example-contributor"
notes: |
Example report — replace with a real one. Boots and installs cleanly from
a USB stick made with the standard ISO. No BIOS changes needed beyond
enabling AHCI mode (should already be default on most T430s).
+78
View File
@@ -0,0 +1,78 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ArchyHCL hardware report",
"description": "One reported install of Archipelago on a physical device. One file per report under data/reports/.",
"type": "object",
"required": ["device_model", "form_factor", "cpu", "ram_gb", "storage", "wifi_chip", "archy_version", "install_method", "status", "tested_date"],
"additionalProperties": false,
"properties": {
"device_model": {
"type": "string",
"description": "Manufacturer + model, e.g. \"Lenovo ThinkPad T430\" or \"Raspberry Pi 5 8GB\"."
},
"form_factor": {
"type": "string",
"enum": ["laptop", "desktop", "mini-pc", "sbc", "server", "other"]
},
"cpu": {
"type": "string",
"description": "e.g. \"Intel Core i5-3320M\", \"Broadcom BCM2712\"."
},
"ram_gb": {
"type": "number",
"exclusiveMinimum": 0
},
"storage": {
"type": "object",
"required": ["type", "size_gb"],
"additionalProperties": false,
"properties": {
"type": { "type": "string", "enum": ["hdd", "ssd", "nvme", "emmc", "sd"] },
"size_gb": { "type": "number", "exclusiveMinimum": 0 }
}
},
"wifi_chip": {
"type": "string",
"description": "Exact chipset if known, e.g. \"Intel Centrino Advanced-N 6205\". Use \"none\" for wired-only setups."
},
"ethernet": {
"type": "string",
"description": "Optional — matters for a node OS more than most HCLs. e.g. \"Intel 82579LM\"."
},
"archy_version": {
"type": "string",
"description": "Exact Archipelago version tested, e.g. \"1.8.4-alpha\"."
},
"install_method": {
"type": "string",
"enum": ["usb-iso", "netboot", "existing-os-script", "other"]
},
"status": {
"type": "string",
"enum": ["working", "partial", "broken"],
"description": "working = installs and runs with no known issues. partial = installs and runs but with specific known issues (detail in `issues`). broken = does not install or does not run at all."
},
"tested_date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"submitted_by": {
"type": "string",
"description": "Optional attribution — nostr npub, Gitea username, or leave unset for anonymous."
},
"issues": {
"type": "string",
"description": "Required detail when status is partial or broken. Free text: what broke, workarounds found."
},
"notes": {
"type": "string",
"description": "Anything else useful: BIOS/UEFI settings needed, quirks, links to a fuller writeup."
}
},
"if": {
"properties": { "status": { "enum": ["partial", "broken"] } }
},
"then": {
"required": ["issues"]
}
}
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Build site/data.json from data/reports/*.yml.
Every report is validated against data/schema.json before being included —
a malformed report fails the build loudly instead of shipping a broken row
to the site (same idea as archy's own scripts/validate-app-manifest.sh:
fail the build, don't ship the drift).
"""
import json
import sys
from pathlib import Path
import yaml
from jsonschema import Draft7Validator
ROOT = Path(__file__).resolve().parent.parent
REPORTS_DIR = ROOT / "data" / "reports"
SCHEMA_PATH = ROOT / "data" / "schema.json"
OUT_PATH = ROOT / "site" / "data.json"
def main() -> int:
schema = json.loads(SCHEMA_PATH.read_text())
validator = Draft7Validator(schema)
reports = []
errors = []
for path in sorted(REPORTS_DIR.glob("*.yml")):
try:
data = yaml.safe_load(path.read_text())
except yaml.YAMLError as e:
errors.append(f"{path.name}: invalid YAML — {e}")
continue
report_errors = sorted(validator.iter_errors(data), key=lambda e: e.path)
if report_errors:
for err in report_errors:
loc = ".".join(str(p) for p in err.path) or "(root)"
errors.append(f"{path.name}: {loc}: {err.message}")
continue
data["_source_file"] = path.name
reports.append(data)
if errors:
print(f"Build failed — {len(errors)} error(s):", file=sys.stderr)
for e in errors:
print(f" {e}", file=sys.stderr)
return 1
reports.sort(key=lambda r: (r["device_model"], r["tested_date"]))
OUT_PATH.write_text(json.dumps(reports, indent=2) + "\n")
print(f"Wrote {len(reports)} report(s) to {OUT_PATH.relative_to(ROOT)}")
return 0
if __name__ == "__main__":
sys.exit(main())
+130
View File
@@ -0,0 +1,130 @@
// No framework, no build step — this is a plain JSON file (site/data.json,
// generated by scripts/build.py) rendered client-side. Keeps the "add a
// device" contribution path as simple as adding one YAML file.
let reports = [];
let sortKey = "device_model";
let sortDir = 1;
const rowsEl = document.getElementById("rows");
const searchEl = document.getElementById("search");
const statusEl = document.getElementById("status-filter");
const formFactorEl = document.getElementById("form-factor-filter");
const countEl = document.getElementById("count");
const emptyEl = document.getElementById("empty");
function storageLabel(storage) {
return `${storage.size_gb}GB ${storage.type.toUpperCase()}`;
}
function matchesFilters(r, query, status, formFactor) {
if (status && r.status !== status) return false;
if (formFactor && r.form_factor !== formFactor) return false;
if (!query) return true;
const haystack = [r.device_model, r.cpu, r.wifi_chip, r.ethernet]
.filter(Boolean)
.join(" ")
.toLowerCase();
return haystack.includes(query);
}
function sortValue(r, key) {
if (key === "storage") return r.storage.size_gb;
return r[key];
}
function render() {
const query = searchEl.value.trim().toLowerCase();
const status = statusEl.value;
const formFactor = formFactorEl.value;
const filtered = reports.filter((r) => matchesFilters(r, query, status, formFactor));
filtered.sort((a, b) => {
const av = sortValue(a, sortKey);
const bv = sortValue(b, sortKey);
if (av < bv) return -1 * sortDir;
if (av > bv) return 1 * sortDir;
return 0;
});
rowsEl.innerHTML = "";
for (const r of filtered) {
const tr = document.createElement("tr");
tr.className = "expandable";
tr.innerHTML = `
<td>${escapeHtml(r.device_model)}</td>
<td>${escapeHtml(r.form_factor)}</td>
<td>${escapeHtml(r.cpu)}</td>
<td>${r.ram_gb}GB</td>
<td>${storageLabel(r.storage)}</td>
<td>${escapeHtml(r.wifi_chip)}</td>
<td><span class="badge ${r.status}">${r.status}</span></td>
<td>${escapeHtml(r.archy_version)}</td>
<td>${escapeHtml(r.tested_date)}</td>
`;
tr.addEventListener("click", () => toggleDetail(tr, r));
rowsEl.appendChild(tr);
}
countEl.textContent = `${filtered.length} of ${reports.length} report${reports.length === 1 ? "" : "s"}`;
emptyEl.hidden = filtered.length !== 0;
}
function toggleDetail(tr, r) {
const next = tr.nextElementSibling;
if (next && next.classList.contains("detail-row")) {
next.remove();
return;
}
document.querySelectorAll(".detail-row").forEach((el) => el.remove());
const detail = document.createElement("tr");
detail.className = "detail-row";
const parts = [];
if (r.ethernet) parts.push(`<strong>Ethernet:</strong> ${escapeHtml(r.ethernet)}`);
parts.push(`<strong>Install method:</strong> ${escapeHtml(r.install_method)}`);
if (r.submitted_by) parts.push(`<strong>Submitted by:</strong> ${escapeHtml(r.submitted_by)}`);
if (r.issues) parts.push(`<strong>Issues:</strong>\n${escapeHtml(r.issues.trim())}`);
if (r.notes) parts.push(`<strong>Notes:</strong>\n${escapeHtml(r.notes.trim())}`);
const td = document.createElement("td");
td.colSpan = 9;
td.innerHTML = parts.join("\n\n");
detail.appendChild(td);
tr.after(detail);
}
function escapeHtml(s) {
const div = document.createElement("div");
div.textContent = s ?? "";
return div.innerHTML;
}
document.querySelectorAll("th[data-sort]").forEach((th) => {
th.addEventListener("click", () => {
const key = th.dataset.sort;
if (sortKey === key) {
sortDir *= -1;
} else {
sortKey = key;
sortDir = 1;
}
render();
});
});
searchEl.addEventListener("input", render);
statusEl.addEventListener("change", render);
formFactorEl.addEventListener("change", render);
fetch("data.json")
.then((res) => res.json())
.then((data) => {
reports = data;
render();
})
.catch((err) => {
emptyEl.hidden = false;
emptyEl.textContent = "Failed to load data.json — " + err;
});
+41
View File
@@ -0,0 +1,41 @@
[
{
"device_model": "Lenovo ThinkPad T430",
"form_factor": "laptop",
"cpu": "Intel Core i5-3320M",
"ram_gb": 8,
"storage": {
"type": "ssd",
"size_gb": 256
},
"wifi_chip": "Intel Centrino Advanced-N 6205",
"ethernet": "Intel 82579LM",
"archy_version": "1.8.4-alpha",
"install_method": "usb-iso",
"status": "working",
"tested_date": "2026-08-15",
"submitted_by": "example-contributor",
"notes": "Example report \u2014 replace with a real one. Boots and installs cleanly from\na USB stick made with the standard ISO. No BIOS changes needed beyond\nenabling AHCI mode (should already be default on most T430s).\n",
"_source_file": "example-thinkpad-t430.yml"
},
{
"device_model": "Raspberry Pi 5 (8GB)",
"form_factor": "sbc",
"cpu": "Broadcom BCM2712 (4x Cortex-A76)",
"ram_gb": 8,
"storage": {
"type": "nvme",
"size_gb": 512
},
"wifi_chip": "Broadcom BCM43455 (onboard)",
"ethernet": "Broadcom onboard Gigabit",
"archy_version": "1.8.4-alpha",
"install_method": "netboot",
"status": "partial",
"tested_date": "2026-08-10",
"submitted_by": "example-contributor",
"issues": "Example report \u2014 replace with a real one. Onboard WiFi drops the\nconnection under sustained heavy P2P traffic (Bitcoin IBD); a workaround\nis to run over Ethernet instead. Everything else (Lightning, apps,\ndashboard) works fine.\n",
"notes": "NVMe via the official PCIe HAT. Boots noticeably faster than SD-card\ninstalls.\n",
"_source_file": "example-raspberry-pi-5.yml"
}
]
+66
View File
@@ -0,0 +1,66 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ArchyHCL — Archipelago Hardware Compatibility List</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>ArchyHCL</h1>
<p>Community-reported hardware compatibility for <a href="https://source.archipelago-foundation.org/lfg2025/archy" target="_blank" rel="noopener">Archipelago</a>. Every row here is a real install someone tested and reported — see it on GitHub/Gitea as a plain data file, no login required to browse.</p>
<p class="cta">
Tested Archipelago on your own hardware? <a href="https://source.archipelago-foundation.org/lfg2025/ArchyHCL/issues/new?template=hardware-report.yml" target="_blank" rel="noopener">Report it</a> —
takes two minutes and helps the next person know what to expect before they buy or repurpose a machine.
</p>
</header>
<main>
<div class="controls">
<input id="search" type="search" placeholder="Search model, CPU, WiFi chip…" autocomplete="off">
<select id="status-filter">
<option value="">All statuses</option>
<option value="working">Working</option>
<option value="partial">Partial</option>
<option value="broken">Broken</option>
</select>
<select id="form-factor-filter">
<option value="">All form factors</option>
<option value="laptop">Laptop</option>
<option value="desktop">Desktop</option>
<option value="mini-pc">Mini PC</option>
<option value="sbc">SBC</option>
<option value="server">Server</option>
<option value="other">Other</option>
</select>
<span id="count" class="count"></span>
</div>
<table id="table">
<thead>
<tr>
<th data-sort="device_model">Device</th>
<th data-sort="form_factor">Type</th>
<th data-sort="cpu">CPU</th>
<th data-sort="ram_gb">RAM</th>
<th data-sort="storage">Storage</th>
<th data-sort="wifi_chip">WiFi chip</th>
<th data-sort="status">Status</th>
<th data-sort="archy_version">Archy ver.</th>
<th data-sort="tested_date">Tested</th>
</tr>
</thead>
<tbody id="rows"></tbody>
</table>
<p id="empty" class="empty" hidden>No reports match your filters.</p>
</main>
<footer>
<p>Data lives in <code>data/reports/*.yml</code>, one file per report — browse or fork the raw data <a href="https://source.archipelago-foundation.org/lfg2025/ArchyHCL/src/branch/main/data/reports" target="_blank" rel="noopener">here</a>. See <a href="https://source.archipelago-foundation.org/lfg2025/ArchyHCL/src/branch/main/CONTRIBUTING.md" target="_blank" rel="noopener">CONTRIBUTING.md</a> for the two ways to add a report.</p>
</footer>
<script src="app.js"></script>
</body>
</html>
+140
View File
@@ -0,0 +1,140 @@
:root {
--bg: #0a0e12;
--panel: #10161c;
--border: #223038;
--text: #d8e4e8;
--muted: #7f97a0;
--accent: #00fff2;
--accent-dim: #00b3a8;
--working: #33d17a;
--partial: #f5b23c;
--broken: #f0506e;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: ui-monospace, "SF Mono", Consolas, monospace;
line-height: 1.5;
}
header, main, footer {
max-width: 1100px;
margin: 0 auto;
padding: 1.5rem;
}
h1 {
color: var(--accent);
letter-spacing: 0.08em;
margin-bottom: 0.25rem;
}
a { color: var(--accent-dim); }
a:hover { color: var(--accent); }
header p { color: var(--muted); max-width: 70ch; }
header .cta { color: var(--text); }
.controls {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
margin-bottom: 1rem;
}
#search {
flex: 1 1 260px;
min-width: 200px;
}
input, select {
background: var(--panel);
border: 1px solid var(--border);
color: var(--text);
padding: 0.5rem 0.7rem;
border-radius: 4px;
font-family: inherit;
font-size: 0.9rem;
}
input:focus, select:focus {
outline: none;
border-color: var(--accent-dim);
}
.count { color: var(--muted); font-size: 0.85rem; margin-left: auto; }
table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
thead th {
text-align: left;
padding: 0.6rem 0.7rem;
border-bottom: 2px solid var(--border);
color: var(--muted);
cursor: pointer;
user-select: none;
white-space: nowrap;
}
thead th:hover { color: var(--accent); }
tbody tr {
border-bottom: 1px solid var(--border);
}
tbody tr:hover { background: var(--panel); }
tbody tr.expandable { cursor: pointer; }
td {
padding: 0.6rem 0.7rem;
vertical-align: top;
}
.badge {
display: inline-block;
padding: 0.15rem 0.5rem;
border-radius: 3px;
font-size: 0.75rem;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.03em;
}
.badge.working { background: rgba(51, 209, 122, 0.15); color: var(--working); }
.badge.partial { background: rgba(245, 178, 60, 0.15); color: var(--partial); }
.badge.broken { background: rgba(240, 80, 110, 0.15); color: var(--broken); }
.detail-row td {
background: var(--panel);
color: var(--muted);
white-space: pre-wrap;
font-size: 0.82rem;
padding: 0.8rem 1.2rem;
}
.detail-row strong { color: var(--text); }
.empty { color: var(--muted); text-align: center; padding: 2rem; }
footer {
color: var(--muted);
font-size: 0.8rem;
border-top: 1px solid var(--border);
margin-top: 2rem;
}
footer code {
background: var(--panel);
padding: 0.1rem 0.35rem;
border-radius: 3px;
}