The backend is a small JSON API: POST a stack, get a job ID, poll it until the build finishes (usually a minute or three).
| Endpoint | What it does |
|---|---|
POST /api/upload | Send multipart/form-data, get back { jobId } |
GET /api/job/{jobId} | Poll it: { status, stage, url, error, warning } |
Fields for /api/upload: stack (the file, required), title (required),
creator, description, category, color
("true" or "false"), and system ("6" or "7").
Wired to the real API. Flip to the Code tab for the HTML and JavaScript behind it.
The HTML, trimmed to the fields the API takes:
<form id="up-form">
<input type="file" name="stack" accept=".zip,.hqx,.sit" required>
<input type="text" name="title" required>
<input type="text" name="creator">
<textarea name="description"></textarea>
<select name="category" required>
<option value="games-humor-fun">Games / Humor / Fun</option>
<!-- ...more categories... -->
</select>
<select name="system">
<option value="7">System 7</option>
<option value="6">System 6</option>
</select>
<input type="hidden" name="color" value="false">
<button type="submit">Build and upload</button>
</form>
And the JavaScript: POST the form, then poll the job until it's done.
const API = "https://hypercard.morphing.cloud";
document.getElementById("up-form").addEventListener("submit", async (e) => {
e.preventDefault();
const res = await fetch(API + "/api/upload", { method: "POST", body: new FormData(e.target) });
const data = await res.json();
if (data.error) return show(data.error);
// poll until the build finishes
const deadline = Date.now() + 5 * 60 * 1000;
while (Date.now() < deadline) {
await new Promise(r => setTimeout(r, 2000));
const job = await fetch(API + "/api/job/" + data.jobId).then(r => r.json());
if (job.status === "done") return show(job.url);
if (job.status === "error") return show(job.error);
show(job.stage || "working");
}
});