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:
1 parent
013695722a
commit
a0d0e707f2
48 files changed
+2724
-228
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { Icon } from "./Icon";
|
||||
import { cycleThemeMode, getThemeMode, onThemeChange } from "../theme";
|
||||
import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
||||
import { get, post } from "../api";
|
||||
@@ -123,7 +124,7 @@ export function Sidebar({ open, activeId, section }: { open: boolean; activeId?:
|
||||
<span>{s.title || t("session.untitled")}</span>
|
||||
</div>
|
||||
<div className="sub">
|
||||
{s.shared && <span title={t("sidebar.shared")}>⇄ </span>}
|
||||
{s.shared && <span title={t("sidebar.shared")}><Icon name="share" className="tiny" /> </span>}
|
||||
{basename(s.cwd)} · {s.agent} · {fmtRelative(s.updated_at)}
|
||||
</div>
|
||||
</button>
|
||||
@@ -138,25 +139,25 @@ export function Sidebar({ open, activeId, section }: { open: boolean; activeId?:
|
||||
</span>
|
||||
<ThemeModeButton />
|
||||
<button className="small ghost" onClick={() => post("/auth/logout").then(() => location.reload())}>
|
||||
<span className="nav-icon" aria-hidden>🚪</span>
|
||||
<Icon name="logout" className="nav-icon" />
|
||||
{t("nav.logout")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="sidebar-foot">
|
||||
<button className={`ghost ${section === "files" ? "active" : ""}`} onClick={() => navigate("/files")}>
|
||||
<span className="nav-icon" aria-hidden>📁</span>
|
||||
<Icon name="folder" className="nav-icon" />
|
||||
{t("nav.files")}
|
||||
</button>
|
||||
<button className={`ghost ${section === "images" ? "active" : ""}`} onClick={() => navigate("/images")}>
|
||||
<span className="nav-icon" aria-hidden>🖼️</span>
|
||||
<Icon name="image" className="nav-icon" />
|
||||
{t("nav.images")}
|
||||
</button>
|
||||
<button className={`ghost ${section === "stats" ? "active" : ""}`} onClick={() => navigate("/stats")}>
|
||||
<span className="nav-icon" aria-hidden>📊</span>
|
||||
<Icon name="chart" className="nav-icon" />
|
||||
{t("nav.stats")}
|
||||
</button>
|
||||
<button className={`ghost ${section === "settings" ? "active" : ""}`} onClick={() => navigate("/settings")}>
|
||||
<span className="nav-icon" aria-hidden>🔧</span>
|
||||
<Icon name="wrench" className="nav-icon" />
|
||||
{t("nav.settings")}
|
||||
</button>
|
||||
</div>
|
||||
@@ -168,7 +169,7 @@ export function Sidebar({ open, activeId, section }: { open: boolean; activeId?:
|
||||
function ThemeModeButton() {
|
||||
const t = useT();
|
||||
const mode = useSyncExternalStore(onThemeChange, getThemeMode);
|
||||
const icon = mode === "auto" ? "◐" : mode === "light" ? "☀" : "☾";
|
||||
const icon = <Icon name={mode === "auto" ? "theme-auto" : mode === "light" ? "sun" : "moon"} />;
|
||||
const label = t(mode === "auto" ? "settings.themeAuto" : mode === "light" ? "settings.themeLight" : "settings.themeDark");
|
||||
return (
|
||||
<button className="small ghost theme-toggle" onClick={cycleThemeMode} title={`${t("settings.themeToggle")} – ${label}`} aria-label={t("settings.themeToggle")}>
|
||||
|
||||
Reference in new issue
Block a user