* feat: AI translations at release, 7 new languages and regional fallback

* fix: proper plural forms for count strings and full translations

* fix: plural forms for selected, hidden and active counts

* fix: synced plugin updates failing on desktop and server-only plugins shown as stopped

* fix: restore rounded corners on app icons and favicon

* feat: desktop app auto updates with automatic, ask first or never modes

* feat: community and custom plugin registries

* feat: plugin guide for AI tools in the SDK with a lint check

* fix: keep the terminal WebSocket open past nginx's 60s default (#1588)

* chore: update changelog

* feat: show plugin download counts only and stop CI builds inflating them

* feat: hide installed plugins in the browse tab by default

* chore: pin bundled plugins to latest releases

* chore: update translations

* fix: focusing a saved password field and saving no longer erases it

* fix: tell desktop users when their 2.8 data needs 2.9 first

* fix: point windows update info at the x64 installer

* fix: keep desktop client tunnel sockets alive behind proxies

* chore: pin bundled plugins to latest releases

* fix: cover client tunnel keepalive in the c2s runtime tests

* fix: typing after opening a host no longer goes to the host search

* fix: accept tab and = separators in SSH config import (#1598)

* chore: update changelog

* chore: drop plugin versions from core changelog

* chore: update translations

* chore: lint, format, and bump version to 26.10.1

---------

Co-authored-by: Darnel Hunter <68697450+innotelinc@users.noreply.github.com>
Co-authored-by: Iain <Iain1970@protonmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-12 11:35:48 -05:00
1 parent 6873bc92bb
commit 62ac931468
174 files changed
+94334 -39995

No files matched your search

@@ -0,0 +1,79 @@
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { BellRing, Download, PauseCircle } from "lucide-react";
import {
desktopUpdates,
useDesktopUpdate,
type DesktopUpdateMode,
} from "@/lib/desktop-updates";
/**
* Desktop only: how the app updates itself. Can be changed later in
* Settings, Updates.
*/
export function DesktopUpdatesStep() {
const { t } = useTranslation();
const state = useDesktopUpdate();
const [picked, setPicked] = useState<DesktopUpdateMode | null>(null);
const mode = picked ?? state?.mode ?? "prompt";
const choose = (next: DesktopUpdateMode) => {
setPicked(next);
void desktopUpdates()
?.setMode(next)
.catch(() => {});
};
const options = [
{
id: "automatic" as const,
icon: Download,
title: t("desktopUpdates.modeAutomatic"),
body: t("desktopUpdates.automaticBody"),
},
{
id: "prompt" as const,
icon: BellRing,
title: t("desktopUpdates.modePrompt"),
body: t("desktopUpdates.promptBody"),
},
{
id: "never" as const,
icon: PauseCircle,
title: t("desktopUpdates.modeNever"),
body: t("desktopUpdates.neverBody"),
},
];
return (
<div className="flex flex-col gap-3">
<p className="text-xs text-muted-foreground">
{state?.kind === "manual"
? t("desktopUpdates.onboardingManual")
: t("desktopUpdates.onboardingBody")}
</p>
<div className="flex flex-col gap-1.5">
{options.map(({ id, icon: Icon, title, body }) => (
<button
key={id}
type="button"
onClick={() => choose(id)}
className={`flex items-start gap-2.5 border p-2.5 text-left transition-colors ${
mode === id
? "border-accent-brand bg-accent-brand/10"
: "border-border bg-card hover:bg-muted/40"
}`}
>
<Icon size={14} className="mt-0.5 shrink-0 text-accent-brand" />
<span className="flex flex-col gap-1">
<span className="text-xs font-medium">{title}</span>
<span className="text-[10px] leading-snug text-muted-foreground">
{body}
</span>
</span>
</button>
))}
</div>
</div>
);
}