Stats history with year filter, folding and XLSX/CSV export; snippets, read-only links, commit suggestions, weekly review, offline copies, server speech-to-text, planning model, job reports; fal.ai health fix
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c08c6c7436
commit
388ca96419
31 files changed
+1518
-82
No files matched your search
@@ -146,8 +146,11 @@ function VersionInfo() {
|
||||
function DigestToggle() {
|
||||
const t = useT();
|
||||
const [on, setOn] = useState<boolean>();
|
||||
const [weekly, setWeekly] = useState(false);
|
||||
useEffect(() => {
|
||||
get<{ enabled: boolean }>("/push/digest").then((r) => setOn(r.enabled)).catch(() => {});
|
||||
get<{ enabled: boolean; weekly?: boolean }>("/push/digest")
|
||||
.then((r) => (setOn(r.enabled), setWeekly(!!r.weekly)))
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
if (on === undefined) return null;
|
||||
return (
|
||||
@@ -164,6 +167,30 @@ function DigestToggle() {
|
||||
/>
|
||||
{t("settings.digest")}
|
||||
</label>
|
||||
<label className="row" style={{ gap: 6, alignItems: "center" }}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={weekly}
|
||||
onChange={(e) => {
|
||||
const v = e.target.checked;
|
||||
setWeekly(v);
|
||||
put("/push/digest", { weekly: v }).catch((err) => (setWeekly(!v), toast(err.message)));
|
||||
}}
|
||||
/>
|
||||
{t("settings.weeklyDigest")}
|
||||
</label>
|
||||
{weekly && (
|
||||
<button
|
||||
className="small ghost"
|
||||
onClick={() =>
|
||||
post<{ sent: number; errors: string[] }>("/push/digest?weekly=true")
|
||||
.then((r) => toast(r.sent ? t("settings.pushTestSent") : r.errors.join("; ") || t("settings.pushTestNone"), r.sent ? "success" : "error"))
|
||||
.catch((e) => toast(e.message))
|
||||
}
|
||||
>
|
||||
{t("settings.digestNow")}
|
||||
</button>
|
||||
)}
|
||||
{on && (
|
||||
<button
|
||||
className="small ghost"
|
||||
@@ -246,6 +273,8 @@ function General({ cfg, reload }: { cfg: ConfigResponse; reload: () => void }) {
|
||||
const [form, setForm] = useState({
|
||||
default_model: s.default_model ?? "",
|
||||
small_model: s.small_model ?? "",
|
||||
plan_model: ((cfg.config as any).agents?.plan?.model ?? "") as string,
|
||||
transcribe_model: ((s as any).transcribe_model ?? "") as string,
|
||||
currency: s.currency ?? "USD",
|
||||
auto_compact_threshold: s.auto_compact_threshold ?? 0.85,
|
||||
commit_trailer: ((s as any).commit_trailer ?? "Co-Authored-By: {model} ({provider}) via CodeCraft") as string,
|
||||
@@ -270,6 +299,8 @@ function General({ cfg, reload }: { cfg: ConfigResponse; reload: () => void }) {
|
||||
savePatches([
|
||||
{ path: ["server", "default_model"], value: form.default_model },
|
||||
{ path: ["server", "small_model"], value: form.small_model },
|
||||
{ path: ["agents", "plan", "model"], value: form.plan_model || null },
|
||||
{ path: ["server", "transcribe_model"], value: form.transcribe_model.trim() },
|
||||
{ path: ["server", "currency"], value: form.currency },
|
||||
{ path: ["server", "auto_compact_threshold"], value: Number(form.auto_compact_threshold) },
|
||||
{ path: ["server", "commit_trailer"], value: form.commit_trailer.trim() },
|
||||
@@ -302,6 +333,12 @@ function General({ cfg, reload }: { cfg: ConfigResponse; reload: () => void }) {
|
||||
<label>{t("settings.smallModel")}</label>
|
||||
<ModelSelect value={form.small_model} onChange={(v) => setForm({ ...form, small_model: v })} />
|
||||
<div className="hint">{t("settings.smallModelHint")}</div>
|
||||
<label>{t("settings.planModel")}</label>
|
||||
<ModelSelect value={form.plan_model} emptyLabel={t("settings.sameAsChat")} onChange={(v) => setForm({ ...form, plan_model: v })} />
|
||||
<div className="hint">{t("settings.planModelHint")}</div>
|
||||
<label>{t("settings.transcribeModel")}</label>
|
||||
<input value={form.transcribe_model} placeholder="openai/whisper-1" onChange={(e) => setForm({ ...form, transcribe_model: e.target.value })} />
|
||||
<div className="hint">{t("settings.transcribeHint")}</div>
|
||||
<label>{t("settings.currency")}</label>
|
||||
<input value={form.currency} onChange={(e) => setForm({ ...form, currency: e.target.value.toUpperCase() })} maxLength={3} />
|
||||
<label>{t("settings.autoCompact")}</label>
|
||||
|
||||
Reference in new issue
Block a user