Add terminal client, memory, model fallback, version and install improvements

- 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>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-09 00:13:06 +01:00
1 parent aa1f8cf016
commit 6dffcb501a
52 files changed
+6477 -765

No files matched your search

+30
View File
@@ -5,6 +5,7 @@ import { useFetch, useModels } from "../hooks";
import { getLang, languages, setLang, useT, type Key } from "../i18n";
import { navigate } from "../router";
import { useStore } from "../store";
import { canPrompt, isInstalled, onInstallChange, platformHint, promptInstall } from "../install";
import { currentSubscription, disablePush, enablePush, pushSupported, testPush } from "../push";
import { applyTheme, getTheme, getThemeMode, onThemeChange, THEME_MODES, THEMES, type Theme, type ThemeMode } from "../theme";
import { setReasoningMode, useReasoningMode, type ReasoningMode } from "../prefs";
@@ -109,12 +110,36 @@ function InterfaceSettings() {
<div className="hint">{t("settings.reasoningHint")}</div>
<label>{t("settings.notifications")}</label>
<PushControl />
<label>{t("install.label")}</label>
<InstallControl />
<label>{t("settings.version")}</label>
<VersionInfo />
<div className="hint">{t("settings.notificationsHint")}</div>
</div>
</div>
);
}
function InstallControl() {
const t = useT();
const prompt = useSyncExternalStore(onInstallChange, canPrompt);
if (isInstalled()) return <span className="chip">{t("install.installed")}</span>;
if (prompt)
return (
<div>
<button className="small primary" onClick={() => void promptInstall()}>
<Icon name="download" /> {t("install.button")}
</button>
</div>
);
return <div className="hint">{t(`install.hint.${platformHint()}` as Key)}</div>;
}
function VersionInfo() {
const info = useFetch<{ version: string; commit?: string }>("/info").data;
return <div className="hint mono">{info ? `CodeCraft ${info.version}${info.commit ? ` (${info.commit})` : ""}` : "…"}</div>;
}
/** Web Push on this device: enable/disable and a test notification. */
function PushControl() {
const t = useT();
@@ -313,6 +338,7 @@ interface ModelRow {
cache_read_price?: number;
cache_write_price?: number;
max_concurrency?: number;
fallback?: string;
options?: Record<string, unknown>;
}
@@ -479,6 +505,7 @@ function ProviderEditor({ name, p, reload }: { name: string; p: Json; reload: ()
<th>{t("settings.priceCacheRead")}</th>
<th>{t("settings.priceCacheWrite")}</th>
<th title={t("settings.maxConcurrencyHint")}>{t("settings.slots")}</th>
<th title={t("settings.fallbackHint")}>{t("settings.fallback")}</th>
<th />
</tr>
</thead>
@@ -516,6 +543,9 @@ function ProviderEditor({ name, p, reload }: { name: string; p: Json; reload: ()
<td>
<input type="number" min={1} value={m.max_concurrency ?? ""} placeholder="∞" onChange={(e) => setModel(i, { max_concurrency: num(e.target.value) })} />
</td>
<td>
<ModelSelect value={m.fallback ?? ""} onChange={(v) => setModel(i, { fallback: v || undefined })} emptyLabel="–" />
</td>
<td>
<button className="small ghost danger" onClick={() => setModels(models.filter((_, j) => j !== i))} aria-label={t("common.remove")}>
<Icon name="close" />