From 733d73ef767387be42cfb83a7c19662e2800166d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 28 Oct 2026 15:42:47 +0500 Subject: [PATCH] Keep window density stable and group update and launch actions --- changelog.d/20261028-split-update-actions.md | 7 ++++ changelog.d/20261028-window-density.md | 7 ++++ src/components/ui/button.tsx | 2 +- src/components/ui/split-button.tsx | 15 ++++++++ src/index.css | 6 ++-- src/screens/Overview.tsx | 38 +++++++------------- src/screens/RealmlistMenu.tsx | 2 +- tools/overview-update-error.test.mjs | 1 + 8 files changed, 47 insertions(+), 31 deletions(-) create mode 100644 changelog.d/20261028-split-update-actions.md create mode 100644 changelog.d/20261028-window-density.md create mode 100644 src/components/ui/split-button.tsx diff --git a/changelog.d/20261028-split-update-actions.md b/changelog.d/20261028-split-update-actions.md new file mode 100644 index 0000000..c3a14f6 --- /dev/null +++ b/changelog.d/20261028-split-update-actions.md @@ -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. diff --git a/changelog.d/20261028-window-density.md b/changelog.d/20261028-window-density.md new file mode 100644 index 0000000..e987242 --- /dev/null +++ b/changelog.d/20261028-window-density.md @@ -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. diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index c9280ed..8511595 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -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" }, diff --git a/src/components/ui/split-button.tsx b/src/components/ui/split-button.tsx new file mode 100644 index 0000000..023bd2e --- /dev/null +++ b/src/components/ui/split-button.tsx @@ -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
+
; +} diff --git a/src/index.css b/src/index.css index 13ef63c..8b4150b 100644 --- a/src/index.css +++ b/src/index.css @@ -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, diff --git a/src/screens/Overview.tsx b/src/screens/Overview.tsx index f1b6057..5185592 100644 --- a/src/screens/Overview.tsx +++ b/src/screens/Overview.tsx @@ -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
{running ? ( - ) : upd.available ? ( - + (needsDecision ? onOpenUpdates() : void runServerUpdate()), children: <>{upd.applying && }{upd.applying ? t("btn.updating") : needsDecision ? t("overview.openUpdates") : t("btn.update")} }} + secondary={{ disabled: transitioning || upd.applying, onClick: () => run("starting"), children: t("btn.startWithoutUpdate") }} + /> ) : ( - )} - {!running && upd.available && ( - - )} {running && ( + void startClientUpdate(server.id), children: t("btn.updateClient") }} + secondary={{ disabled: realmBusy || playing || upd.applying, onClick: () => void play(), children: <>{playing && }{t("btn.playWithoutUpdate")} }} + /> ) : client ? ( ) : null} - {client && !jobBusy && (clientStatus?.update_available || job.phase === "choose") && ( - - )}
diff --git a/src/screens/RealmlistMenu.tsx b/src/screens/RealmlistMenu.tsx index b230f94..faf8530 100644 --- a/src/screens/RealmlistMenu.tsx +++ b/src/screens/RealmlistMenu.tsx @@ -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" > {t("realm.label")} diff --git a/tools/overview-update-error.test.mjs b/tools/overview-update-error.test.mjs index d5d52d0..eabc282 100644 --- a/tools/overview-update-error.test.mjs +++ b/tools/overview-update-error.test.mjs @@ -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) {