fix(updates): keep the update modal's footer buttons on one line

The footer wrapped every label mid-word ("Check / now", "Copy / command")
because nothing stopped the text breaking and the modal was too narrow to
hold four buttons.

Three changes, smallest first:

  - `whitespace-nowrap` on the footer container. `white-space` inherits, so
    one class keeps every label on its own line without touching each button.
  - `flex-wrap` alongside it, so a translation that still outgrows the modal
    breaks the ROW rather than the words.
  - `max-w-lg` -> `max-w-2xl` on the dialog.

Those alone were not enough in Vietnamese: the button's restarting label is
a full sentence ("Đã build xong — đang khởi động lại bảng điều khiển...",
52 chars), which put the row over the width on its own. That sentence is
already shown in the status banner directly above the footer, so the button
now uses a new short `restartingShort` key and the banner keeps the full
one unchanged.
This commit is contained in:
2026-09-14 16:32:15 +07:00
parent e895c0e8eb
commit 13bc7154d6
3 changed files with 15 additions and 5 deletions
+11 -3
View File
@@ -306,7 +306,9 @@ export function UpdateNotifier() {
if (e.target === e.currentTarget) dismiss(); if (e.target === e.currentTarget) dismiss();
}} }}
> >
<div className="w-full max-w-lg card shadow-2xl animate-slide-up overflow-hidden"> {/* max-w-2xl, not lg: the footer carries up to four buttons and the
longest label is a whole sentence ("restarting the dashboard…"). */}
<div className="w-full max-w-2xl card shadow-2xl animate-slide-up overflow-hidden">
{/* Header */} {/* Header */}
<div className="flex items-start justify-between gap-3 px-5 py-4 border-b border-border"> <div className="flex items-start justify-between gap-3 px-5 py-4 border-b border-border">
<div className="flex items-center gap-3 min-w-0"> <div className="flex items-center gap-3 min-w-0">
@@ -432,7 +434,10 @@ export function UpdateNotifier() {
</div> </div>
{/* Footer */} {/* Footer */}
<div className="flex items-center justify-end gap-2 px-5 py-3 border-t border-border bg-surface-2/40"> {/* `whitespace-nowrap` inherits, so one class keeps every label on its
own single line; `flex-wrap` then breaks the ROW instead of the
words if a translation still outgrows the modal. */}
<div className="flex flex-wrap items-center justify-end gap-2 whitespace-nowrap px-5 py-3 border-t border-border bg-surface-2/40">
<button <button
type="button" type="button"
onClick={checkNow} onClick={checkNow}
@@ -473,7 +478,10 @@ export function UpdateNotifier() {
) : ( ) : (
<Zap className="w-4 h-4" aria-hidden /> <Zap className="w-4 h-4" aria-hidden />
)} )}
{applying ? t("updating") : restarting ? t("restarting") : t("updateNow")} {/* `restartingShort`, not `restarting`: the full sentence is
already in the banner above, and a whole sentence on a button
is what forced this footer onto two lines. */}
{applying ? t("updating") : restarting ? t("restartingShort") : t("updateNow")}
</button> </button>
) : null} ) : null}
</div> </div>
+2 -1
View File
@@ -25,5 +25,6 @@
"releaseUnavailable": "Could not read the latest release from the repository ({{reason}}); fell back to comparing commits.", "releaseUnavailable": "Could not read the latest release from the repository ({{reason}}); fell back to comparing commits.",
"pluginAutoNote": "This dashboard is installed as a Claude Code plugin. \"Update now\" pulls the new plugin version, reinstalls the runtime, and restarts the server for you — it takes a few minutes. Newly added skills and slash commands only appear in an already-open Claude Code session after you run /reload-plugins there.", "pluginAutoNote": "This dashboard is installed as a Claude Code plugin. \"Update now\" pulls the new plugin version, reinstalls the runtime, and restarts the server for you — it takes a few minutes. Newly added skills and slash commands only appear in an already-open Claude Code session after you run /reload-plugins there.",
"pluginUpdating": "Pulling the new plugin version and reinstalling the runtime — this takes a few minutes, the page reloads itself when the new server is up...", "pluginUpdating": "Pulling the new plugin version and reinstalling the runtime — this takes a few minutes, the page reloads itself when the new server is up...",
"lastPluginUpdateFailed": "The last plugin update failed at step \"{{step}}\": {{error}} — run /agenthub-update in Claude Code to recover." "lastPluginUpdateFailed": "The last plugin update failed at step \"{{step}}\": {{error}} — run /agenthub-update in Claude Code to recover.",
"restartingShort": "Restarting…"
} }
+2 -1
View File
@@ -25,5 +25,6 @@
"releaseUnavailable": "Không đọc được bản phát hành mới nhất từ repo ({{reason}}); đã quay về so sánh theo commit.", "releaseUnavailable": "Không đọc được bản phát hành mới nhất từ repo ({{reason}}); đã quay về so sánh theo commit.",
"pluginAutoNote": "Dashboard này cài dạng plugin của Claude Code. Bấm \"Cập nhật ngay\" sẽ tự tải bản plugin mới, cài lại runtime và khởi động lại server — mất vài phút. Riêng skill và slash command mới thì session Claude Code đang mở chỉ thấy sau khi bạn chạy /reload-plugins ở đó.", "pluginAutoNote": "Dashboard này cài dạng plugin của Claude Code. Bấm \"Cập nhật ngay\" sẽ tự tải bản plugin mới, cài lại runtime và khởi động lại server — mất vài phút. Riêng skill và slash command mới thì session Claude Code đang mở chỉ thấy sau khi bạn chạy /reload-plugins ở đó.",
"pluginUpdating": "Đang tải bản plugin mới và cài lại runtime — mất vài phút, trang sẽ tự reload khi server mới lên...", "pluginUpdating": "Đang tải bản plugin mới và cài lại runtime — mất vài phút, trang sẽ tự reload khi server mới lên...",
"lastPluginUpdateFailed": "Lần cập nhật plugin trước thất bại ở bước \"{{step}}\": {{error}} — chạy /agenthub-update trong Claude Code để khắc phục." "lastPluginUpdateFailed": "Lần cập nhật plugin trước thất bại ở bước \"{{step}}\": {{error}} — chạy /agenthub-update trong Claude Code để khắc phục.",
"restartingShort": "Đang khởi động lại…"
} }