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

+37
View File
@@ -97,6 +97,13 @@ impl Store {
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS queue_session ON queue(session_id, created_at);
CREATE TABLE IF NOT EXISTS push_subs (
endpoint TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
p256dh TEXT NOT NULL,
auth TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS audit (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER NOT NULL,
@@ -874,6 +881,30 @@ impl Store {
// --- audit log & backup ----------------------------------------------------------
impl Store {
// --- web push ---------------------------------------------------------
pub fn push_add(&self, user_id: &str, endpoint: &str, p256dh: &str, auth: &str) -> Result<()> {
let conn = self.conn.lock().unwrap();
conn.execute(
"INSERT INTO push_subs (endpoint, user_id, p256dh, auth, created_at) VALUES (?1, ?2, ?3, ?4, ?5)
ON CONFLICT(endpoint) DO UPDATE SET user_id = ?2, p256dh = ?3, auth = ?4",
rusqlite::params![endpoint, user_id, p256dh, auth, now_ms()],
)?;
Ok(())
}
pub fn push_remove(&self, endpoint: &str) -> Result<()> {
self.conn.lock().unwrap().execute("DELETE FROM push_subs WHERE endpoint = ?1", [endpoint])?;
Ok(())
}
pub fn push_subscriptions(&self, user_id: &str) -> Result<Vec<PushSub>> {
let conn = self.conn.lock().unwrap();
let mut stmt = conn.prepare("SELECT endpoint, p256dh, auth FROM push_subs WHERE user_id = ?1")?;
let rows = stmt.query_map([user_id], |r| Ok(PushSub { endpoint: r.get(0)?, p256dh: r.get(1)?, auth: r.get(2)? }))?;
Ok(rows.collect::<std::result::Result<_, _>>()?)
}
pub fn audit(&self, user_id: Option<&str>, username: &str, action: &str, detail: &str, ip: &str) {
let conn = self.conn.lock().unwrap();
let _ = conn.execute(
@@ -946,3 +977,9 @@ impl Store {
Ok(data.and_then(|d| serde_json::from_str::<Value>(&d).ok()).and_then(|v| v["created_at"].as_i64()))
}
}
pub struct PushSub {
pub endpoint: String,
pub p256dh: String,
pub auth: String,
}