- codecraft: full-screen terminal client (ratatui) – local tools and files, models via the CodeCraft server gateway or own providers (standalone with a guided setup), slash commands, @files, permission dialogs, resume, update - Static Linux CLI builds (x86_64/aarch64, cargo-zigbuild) served by the server: /cli/install.sh and /cli/download/<target> - Project memory (remember tool, .codecraft/memory.md) and personal memory - Model fallback when a host is unreachable or a budget is used up - Version/commit in UI and health, daily release check, "What's new" dialog - PWA: install button, maskable icon, manifest id and screenshots - Case-insensitive detection of connection errors for retries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
import { useState } from "react";
|
|
import { getLang, useT } from "../i18n";
|
|
import { WHATS_NEW } from "../whatsnew";
|
|
import { Icon } from "./Icon";
|
|
|
|
const KEY = "codecraft.seenVersion";
|
|
|
|
/** Shown once after the server was updated to a version with release notes. */
|
|
export function WhatsNew({ version }: { version: string }) {
|
|
const t = useT();
|
|
const seen = localStorage.getItem(KEY);
|
|
const [open, setOpen] = useState(() => {
|
|
// First visit ever: just remember the version.
|
|
if (!seen) {
|
|
localStorage.setItem(KEY, version);
|
|
return false;
|
|
}
|
|
return seen !== version && !!WHATS_NEW[version];
|
|
});
|
|
if (!open) return null;
|
|
const entry = WHATS_NEW[version];
|
|
const items = getLang() === "de" ? entry.de : entry.en;
|
|
const close = () => {
|
|
localStorage.setItem(KEY, version);
|
|
setOpen(false);
|
|
};
|
|
return (
|
|
<div className="modal-backdrop" onClick={close}>
|
|
<div className="modal" role="dialog" aria-label={t("whatsnew.title")} onClick={(e) => e.stopPropagation()}>
|
|
<h3>
|
|
<Icon name="sparkle" /> {t("whatsnew.title", { version })}
|
|
</h3>
|
|
<ul className="whatsnew">
|
|
{items.map((i) => (
|
|
<li key={i}>{i}</li>
|
|
))}
|
|
</ul>
|
|
<div className="row" style={{ justifyContent: "flex-end" }}>
|
|
<button className="primary" onClick={close}>
|
|
{t("common.ok")}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|