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

+14 -7
View File
@@ -1,3 +1,4 @@
import { Icon } from "./Icon";
import { base64Size, FileFigure } from "./FileFigure";
import { useT } from "../i18n";
import { navigate } from "../router";
@@ -26,6 +27,12 @@ export function toolTitle(name: string, input: any): string {
return input?.path ?? ".";
case "bash":
return input?.description || input?.command || "";
case "craft_phase":
return `${input?.phase ?? ""}${input?.note ? ` – ${input.note}` : ""}`;
case "web_search":
return input?.query ?? "";
case "diagnostics":
return input?.path ?? "";
case "glob":
case "grep":
return `${input?.pattern ?? ""}${input?.path ? ` in ${input.path}` : ""}`;
@@ -35,7 +42,7 @@ export function toolTitle(name: string, input: any): string {
case "task":
return `${input?.subagent_type ?? "general"}: ${input?.description ?? ""}`;
case "image_compare":
return `${input?.original ?? ""} ↔ ${input?.candidate ?? ""}`;
return `${input?.original ?? ""} vs. ${input?.candidate ?? ""}`;
case "skill":
return input?.name ?? "";
case "ask_user":
@@ -46,12 +53,12 @@ export function toolTitle(name: string, input: any): string {
}
export function TodoList({ todos }: { todos: Todo[] }) {
const icon = { completed: "☑", in_progress: "▶", pending: "☐" } as const;
const icon = { completed: "box-on", in_progress: "play", pending: "box-off" } as const;
return (
<ul>
{todos.map((td, i) => (
<li key={i} className={td.status}>
<span>{icon[td.status] ?? "☐"}</span>
<Icon name={icon[td.status as keyof typeof icon] ?? "box-off"} />
<span>{td.content}</span>
</li>
))}
@@ -63,7 +70,7 @@ export function ToolCard({ call, result, running, baseDir }: { call: ToolCall; r
const t = useT();
const progress = useStore((s) => s.progress[call.id]);
const status = result ? (result.is_error ? "err" : "ok") : running ? "run" : "err";
const statusText = result ? (result.is_error ? "✗" : "✓") : running ? "…" : "–";
const statusText = result ? <Icon name={result.is_error ? "x-mark" : "check"} /> : running ? "…" : "–";
const meta = result?.meta;
// The live task list has its own panel; updates are a single line here.
@@ -73,8 +80,8 @@ export function ToolCard({ call, result, running, baseDir }: { call: ToolCall; r
const current = todos.find((x) => x.status === "in_progress");
return (
<div className="todo-line">
☑ {t("tool.todosUpdated", { done, total: todos.length })}
{current && <span> · ▶ {current.content}</span>}
<Icon name="box-on" /> {t("tool.todosUpdated", { done, total: todos.length })}
{current && <span> · <Icon name="play" className="tiny" /> {current.content}</span>}
</div>
);
}
@@ -90,7 +97,7 @@ export function ToolCard({ call, result, running, baseDir }: { call: ToolCall; r
<div className="tool-head">
<span className="tool-name">{call.name}</span>
<span className="tool-title">{call.input.path}</span>
<span className="status ok">✓</span>
<span className="status ok"><Icon name="check" /></span>
</div>
<FileFigure url={writtenImage} name={call.input.path}>
<img src={`${writtenImage}&v=${result!.call_id}`} alt={call.input.path} />