Keep window density stable and group update and launch actions

This commit is contained in:
Дмитрий committed 2026-10-28 15:42:47 +05:00
1 parent 5525325160
commit 733d73ef76
8 files changed
+47 -31

No files matched your search

@@ -0,0 +1,7 @@
---
area: manager
type: changed
audience: admins
title: Update and launch without updating share a divided button
---
Server and game client actions each use two adjacent segments of equal height. Both actions remain visible and keep their own availability checks.
+7
View File
@@ -0,0 +1,7 @@
---
area: manager
type: fixed
audience: admins
title: Maximizing the window keeps text and controls at a comfortable size
---
The interface no longer enlarges with window width. System display scaling and browser zoom still work, and large action buttons use more compact spacing.
+1 -1
View File
@@ -15,7 +15,7 @@ const buttonVariants = cva(
size: {
sm: "h-9 px-3 text-sm",
md: "h-11 px-5 text-[15px]",
xl: "h-16 px-10 text-lg tracking-wide",
xl: "h-12 px-6 text-base",
},
},
defaultVariants: { variant: "secondary", size: "md" },
+15
View File
@@ -0,0 +1,15 @@
import { Button, type ButtonProps } from "./button";
/** Two explicit actions, each with its own enabled state and keyboard focus. */
export function SplitButton({ label, primary, secondary }: {
label: string;
primary: ButtonProps;
secondary: ButtonProps;
}) {
return <div role="group" aria-label={label} className="inline-flex max-w-full items-stretch rounded-md">
<Button type="button" variant="primary" {...primary}
className="h-auto min-h-12 min-w-0 rounded-r-none px-5 py-2 text-sm whitespace-normal" />
<Button type="button" variant="secondary" {...secondary}
className="h-auto min-h-12 min-w-0 rounded-l-none border-l border-l-gold/30 px-4 py-2 text-sm whitespace-normal" />
</div>;
}
+3 -3
View File
@@ -16,10 +16,10 @@
--font-sans: "Segoe UI Variable Text", "Segoe UI", "Microsoft YaHei UI", "Microsoft YaHei", system-ui, sans-serif;
}
/* Everything is sized in rem, so scaling the root font grows the whole interface with the window
(16px up to about 1280px wide, then gradually larger; capped at 26px). */
/* Window size changes layout, not UI density. Keep rem tied to the user's default
font size; browser zoom and operating-system display scaling still apply. */
html {
font-size: clamp(16px, calc(0.75vw + 6.4px), 26px);
font-size: 100%;
}
html,
+12 -26
View File
@@ -15,6 +15,7 @@ import { PublishRealmDialog } from "@/screens/PublishDialog";
import { useRegistryStatus } from "@/lib/registry";
import { usePublishRealms, type PublishRealm } from "@/lib/publishRealms";
import { useAutoRestart } from "@/lib/autoRestartPrefs";
import { SplitButton } from "@/components/ui/split-button";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
@@ -410,32 +411,21 @@ export function Overview({ server, companions = true, onForget, onOpenUpdates, o
<div className="mt-7 flex flex-wrap items-center gap-x-4 gap-y-3">
{running ? (
<Button variant="secondary" size="xl" disabled={transitioning} onClick={() => run("stopping")} className="min-w-56">
<Button variant="secondary" size="xl" disabled={transitioning} onClick={() => run("stopping")} className="min-w-40">
{transitioning && <Loader2 className="h-5 w-5 animate-spin" aria-hidden />}
{t("btn.stop")}
</Button>
) : upd.available ? (
<Button
variant="primary"
size="xl"
disabled={transitioning || upd.applying}
onClick={() => (needsDecision ? onOpenUpdates() : void runServerUpdate())}
className="min-w-56"
>
{upd.applying && <Loader2 className="h-5 w-5 animate-spin" aria-hidden />}
{upd.applying ? t("btn.updating") : needsDecision ? t("overview.openUpdates") : t("btn.update")}
</Button>
<SplitButton label={t("overview.server")}
primary={{ disabled: transitioning || upd.applying, onClick: () => (needsDecision ? onOpenUpdates() : void runServerUpdate()), children: <>{upd.applying && <Loader2 className="h-4 w-4 animate-spin" aria-hidden />}{upd.applying ? t("btn.updating") : needsDecision ? t("overview.openUpdates") : t("btn.update")}</> }}
secondary={{ disabled: transitioning || upd.applying, onClick: () => run("starting"), children: t("btn.startWithoutUpdate") }}
/>
) : (
<Button variant="primary" size="xl" disabled={transitioning} onClick={() => run("starting")} className="min-w-56">
<Button variant="primary" size="xl" disabled={transitioning} onClick={() => run("starting")} className="min-w-40">
{transitioning && <Loader2 className="h-5 w-5 animate-spin" aria-hidden />}
{transitioning ? (action === "stopping" ? t("btn.stoppingCaps") : t("btn.startingCaps")) : t("btn.start")}
</Button>
)}
{!running && upd.available && (
<Button variant="secondary" size="sm" disabled={transitioning || upd.applying} onClick={() => run("starting")}>
{t("btn.startWithoutUpdate")}
</Button>
)}
{running && (
<Button variant="ghost" size="sm" disabled={transitioning || upd.applying} onClick={() => run("restarting")}>
{t("btn.restart")}
@@ -456,7 +446,7 @@ export function Overview({ server, companions = true, onForget, onOpenUpdates, o
{client && <RealmlistMenu serverId={server.id} />}
{client && jobBusy ? (
<div
className="relative flex h-16 min-w-64 items-center overflow-hidden rounded-md border border-gold/50 bg-card-2"
className="relative flex h-12 min-w-64 items-center overflow-hidden rounded-md border border-gold/50 bg-card-2"
role="progressbar"
aria-valuenow={jobPct}
aria-valuemin={0}
@@ -482,9 +472,10 @@ export function Overview({ server, companions = true, onForget, onOpenUpdates, o
</button>
</div>
) : client && (clientStatus?.update_available || job.phase === "choose") ? (
<Button variant="primary" size="xl" disabled={playing || job.phase === "choose"} onClick={() => void startClientUpdate(server.id)} className="min-w-40">
{t("btn.updateClient")}
</Button>
<SplitButton label={t("btn.play")}
primary={{ disabled: playing || job.phase === "choose", onClick: () => void startClientUpdate(server.id), children: t("btn.updateClient") }}
secondary={{ disabled: realmBusy || playing || upd.applying, onClick: () => void play(), children: <>{playing && <Loader2 className="h-4 w-4 animate-spin" aria-hidden />}{t("btn.playWithoutUpdate")}</> }}
/>
) : client ? (
<Button variant="primary" size="xl" disabled={realmBusy || playing} onClick={() => void play()} className="min-w-40">
{playing && <Loader2 className="h-5 w-5 animate-spin" aria-hidden />}
@@ -495,11 +486,6 @@ export function Overview({ server, companions = true, onForget, onOpenUpdates, o
{t("btn.setupClient")}
</Button>
) : null}
{client && !jobBusy && (clientStatus?.update_available || job.phase === "choose") && (
<Button variant="secondary" size="sm" disabled={realmBusy || playing || upd.applying} onClick={() => void play()}>
{t("btn.playWithoutUpdate")}
</Button>
)}
</div>
</div>
<div className="mt-6 flex flex-wrap items-center justify-between gap-3 border-t border-line/60 pt-4 text-xs text-muted">
+1 -1
View File
@@ -92,7 +92,7 @@ export function RealmlistMenu({ serverId, className }: { serverId: string; class
onClick={() => setOpen((v) => !v)}
aria-haspopup="menu"
aria-expanded={open}
className="flex h-16 cursor-pointer items-center gap-3 rounded-md border border-line bg-card-2 px-4 text-left transition-colors hover:border-gold/50"
className="flex h-12 cursor-pointer items-center gap-3 rounded-md border border-line bg-card-2 px-4 text-left transition-colors hover:border-gold/50"
>
<span>
<span className="block text-[11px] uppercase tracking-wide text-muted">{t("realm.label")}</span>
+1
View File
@@ -28,6 +28,7 @@ const bundle = await rolldown({
external: (id, importer) => id === "react/jsx-runtime" || id === "lucide-react" || (id === "react" && !importer?.endsWith("Overview.tsx")),
plugins: [{ name: "overview-test",
resolveId(id, importer) {
if (id === "@/components/ui/split-button") return fileURLToPath(new URL("../src/components/ui/split-button.tsx", import.meta.url));
if (id.startsWith("@/") || (id === "react" && importer?.endsWith("Overview.tsx"))) return "\0" + id;
},
load(id) {