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:
1 parent
aa1f8cf016
commit
6dffcb501a
52 files changed
+6477
-765
No files matched your search
@@ -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" />
|
||||
|
||||
Reference in new issue
Block a user