Add Web Push, guided start screen, SVG icons, chunked uploads, rename

- Web Push (VAPID + RFC 8291, pure Rust): permission/question/finished
  notifications while no app window is visible; settings with test button
- Guided new-session flow: task, craft target (website/app/game/old
  software/fresh), clean room or deep, description, model, folder
  (new, existing or cloned git repository); originals upload to original/
- Craft phase bar (craft_phase tool) for study/specify/build/check/ready
- Chunked uploads (8 MB) up to server.max_upload_mb (default 500 MB)
- All UI glyphs and emojis replaced by themeable SVG icons
- Rename dialog with manual name or model-generated suggestion

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-08 21:03:26 +01:00
1 parent 013695722a
commit a0d0e707f2
48 files changed
+2724 -228

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
// Web Push handling, imported into the generated service worker (workbox importScripts).
self.addEventListener("push", (event) => {
let data = {};
try {
data = event.data ? event.data.json() : {};
} catch {
data = { body: event.data ? event.data.text() : "" };
}
const title = data.title || "CodeCraft";
const prefix = { permission: "🔐 ", question: "❓ ", done: "✅ " }[data.kind] || "";
event.waitUntil(
self.registration.showNotification(prefix + title, {
body: data.kind === "done" ? data.body || "Finished" : data.body || "",
tag: data.tag || "codecraft",
renotify: true,
icon: "/icon-192.png",
badge: "/icon-192.png",
data: { url: data.url || "/" },
}),
);
});
self.addEventListener("notificationclick", (event) => {
event.notification.close();
const url = new URL((event.notification.data && event.notification.data.url) || "/", self.location.origin).href;
event.waitUntil(
self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((list) => {
for (const client of list) {
if (new URL(client.url).origin === self.location.origin) {
return client.focus().then((c) => (c && "navigate" in c ? c.navigate(url) : c));
}
}
return self.clients.openWindow(url);
}),
);
});