mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-30 15:15:24 +00:00
v26.10.1 (#1599)
* 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:
174 files changed
+94334
-39995
No files matched your search
@@ -1,10 +1,19 @@
|
||||
import { LayoutTemplate, Monitor, Palette, Puzzle, Shield } from "lucide-react";
|
||||
import {
|
||||
LayoutTemplate,
|
||||
Monitor,
|
||||
MonitorDown,
|
||||
Palette,
|
||||
Puzzle,
|
||||
Shield,
|
||||
} from "lucide-react";
|
||||
import { CORE_ONBOARDING_STEPS } from "@/types/onboarding";
|
||||
import { PluginsStep } from "./steps/PluginsStep";
|
||||
import { PresetStep } from "./steps/PresetStep";
|
||||
import { AppearanceStep } from "./steps/AppearanceStep";
|
||||
import { SecurityStep } from "./steps/SecurityStep";
|
||||
import { DesktopSyncStep } from "./steps/DesktopSyncStep";
|
||||
import { DesktopUpdatesStep } from "./steps/DesktopUpdatesStep";
|
||||
import { desktopUpdates } from "@/lib/desktop-updates";
|
||||
import type { OnboardingStepDef } from "./onboarding-registry";
|
||||
|
||||
/**
|
||||
@@ -38,6 +47,17 @@ export const CORE_STEPS: OnboardingStepDef[] = [
|
||||
position: 20,
|
||||
isRelevant: (ctx) => ctx.isDesktop,
|
||||
},
|
||||
{
|
||||
id: "desktop-updates",
|
||||
version: CORE_ONBOARDING_STEPS["desktop-updates"],
|
||||
titleKey: "desktopUpdates.onboardingTitle",
|
||||
icon: MonitorDown,
|
||||
Component: DesktopUpdatesStep,
|
||||
audience: "all",
|
||||
position: 25,
|
||||
// App Store builds have no updater.
|
||||
isRelevant: (ctx) => ctx.isDesktop && desktopUpdates() !== null,
|
||||
},
|
||||
{
|
||||
id: "preset",
|
||||
version: CORE_ONBOARDING_STEPS.preset,
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user