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
@@ -4,6 +4,8 @@ import { useT } from "../../i18n";
|
||||
import { useStore } from "../../store";
|
||||
import { GitAccount } from "./GitAccount";
|
||||
import { ApiTokens } from "./ApiTokens";
|
||||
import { CliInstall } from "./CliInstall";
|
||||
import { Memory } from "./Memory";
|
||||
|
||||
export function Account() {
|
||||
const t = useT();
|
||||
@@ -46,7 +48,9 @@ export function Account() {
|
||||
</button>
|
||||
<div style={{ fontSize: 12, color: "var(--muted)", marginTop: 8 }}>{t("account.hint")}</div>
|
||||
</div>
|
||||
<Memory />
|
||||
<GitAccount />
|
||||
<CliInstall />
|
||||
<ApiTokens />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useT } from "../../i18n";
|
||||
import { toast } from "../../dialogs";
|
||||
import { Icon } from "../Icon";
|
||||
|
||||
/** How to install and use the terminal client. */
|
||||
export function CliInstall() {
|
||||
const t = useT();
|
||||
const cmd = `curl -fsSL ${location.origin}/cli/install.sh | sh`;
|
||||
const copy = (s: string) => navigator.clipboard.writeText(s).then(() => toast(t("tokens.copied"), "success"));
|
||||
return (
|
||||
<div className="panel">
|
||||
<h3>
|
||||
<Icon name="terminal" /> {t("cli.title")}
|
||||
</h3>
|
||||
<div className="hint" style={{ marginBottom: 10 }}>
|
||||
{t("cli.intro")}
|
||||
</div>
|
||||
<ol className="cli-steps">
|
||||
<li>
|
||||
{t("cli.step1")}
|
||||
<div className="row" style={{ gap: 6, marginTop: 4 }}>
|
||||
<code className="token-value">{cmd}</code>
|
||||
<button className="small" onClick={() => copy(cmd)}>
|
||||
<Icon name="copy" /> {t("tokens.copy")}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
{t("cli.step2")}
|
||||
<div className="row" style={{ gap: 6, marginTop: 4 }}>
|
||||
<code className="token-value">{`codecraft login ${location.origin}`}</code>
|
||||
<button className="small" onClick={() => copy(`codecraft login ${location.origin}`)}>
|
||||
<Icon name="copy" /> {t("tokens.copy")}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li>{t("cli.step3")}</li>
|
||||
</ol>
|
||||
<div className="hint">
|
||||
{t("cli.downloads")} <a href="/cli/download/linux-x86_64">Linux x86_64</a> · <a href="/cli/download/linux-aarch64">Linux arm64</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { get, put } from "../../api";
|
||||
import { useT } from "../../i18n";
|
||||
import { toast } from "../../dialogs";
|
||||
import { Icon } from "../Icon";
|
||||
|
||||
/** Personal memory: preferences that apply in every project (web and terminal). */
|
||||
export function Memory() {
|
||||
const t = useT();
|
||||
const [text, setText] = useState<string>();
|
||||
useEffect(() => {
|
||||
get<{ memory: string }>("/account/memory")
|
||||
.then((r) => setText(r.memory ?? ""))
|
||||
.catch((e) => toast(e.message));
|
||||
}, []);
|
||||
if (text === undefined) return null;
|
||||
return (
|
||||
<div className="panel">
|
||||
<h3>
|
||||
<Icon name="brain" /> {t("memory.title")}
|
||||
</h3>
|
||||
<div className="hint" style={{ marginBottom: 8 }}>
|
||||
{t("memory.hint")}
|
||||
</div>
|
||||
<textarea rows={6} style={{ width: "100%" }} value={text} placeholder={t("memory.placeholder")} onChange={(e) => setText(e.target.value)} />
|
||||
<div className="row" style={{ marginTop: 8 }}>
|
||||
<span className="grow" />
|
||||
<button className="primary" onClick={() => put("/account/memory", { memory: text }).then(() => toast(t("settings.saved"), "success")).catch((e) => toast(e.message))}>
|
||||
{t("common.save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user