fix(storage): StoragePanel rows wrap on narrow viewports

Upload Limit, Stale Uploads, and Media Retention rows now use
flex-wrap with gap-x-3 gap-y-2; input + unit pairs that must stay
together are wrapped in a nested flex group so they migrate as a unit.
Desktop spacing identical (gap-x-3 ≡ original gap-3 when content fits
on one line).
This commit is contained in:
Jannis Braun
2026-05-05 23:24:10 +02:00
parent 899dc8b601
commit 438e7fb113
@@ -262,31 +262,33 @@ export function StoragePanel() {
<div> <div>
<div className="text-[11px] font-semibold text-txt-tertiary uppercase tracking-wider mb-1.5">Upload Limit</div> <div className="text-[11px] font-semibold text-txt-tertiary uppercase tracking-wider mb-1.5">Upload Limit</div>
<div className="rounded-lg bg-white/[0.02] p-3.5"> <div className="rounded-lg bg-white/[0.02] p-3.5">
<div className="flex items-center gap-3"> <div className="flex flex-wrap items-center gap-x-3 gap-y-2">
<label className="text-sm text-txt-secondary whitespace-nowrap">Max file size</label> <label className="text-sm text-txt-secondary whitespace-nowrap">Max file size</label>
<input <div className="flex items-center gap-3">
type="number" <input
min={uploadUnit === 'GB' ? 0.001 : 1} type="number"
step={uploadUnit === 'GB' ? 0.5 : 1} min={uploadUnit === 'GB' ? 0.001 : 1}
value={uploadLimitInput} step={uploadUnit === 'GB' ? 0.5 : 1}
onChange={(e) => setUploadLimitInput(e.target.value)} value={uploadLimitInput}
className="input-standard w-24 px-2 py-1 text-sm text-center" onChange={(e) => setUploadLimitInput(e.target.value)}
/> className="input-standard w-24 px-2 py-1 text-sm text-center"
<div className="flex items-center gap-0.5 rounded-lg bg-surface-input p-0.5"> />
{(['MB', 'GB'] as const).map((u) => ( <div className="flex items-center gap-0.5 rounded-lg bg-surface-input p-0.5">
<button {(['MB', 'GB'] as const).map((u) => (
key={u} <button
type="button" key={u}
onClick={() => handleUploadUnitChange(u)} type="button"
className={`px-2.5 py-1 rounded text-[12px] font-medium transition-colors ${ onClick={() => handleUploadUnitChange(u)}
uploadUnit === u className={`px-2.5 py-1 rounded text-[12px] font-medium transition-colors ${
? 'bg-accent-primary text-white' uploadUnit === u
: 'text-txt-tertiary hover:text-txt-primary' ? 'bg-accent-primary text-white'
}`} : 'text-txt-tertiary hover:text-txt-primary'
> }`}
{u} >
</button> {u}
))} </button>
))}
</div>
</div> </div>
<button <button
onClick={handleUploadLimitSave} onClick={handleUploadLimitSave}
@@ -308,7 +310,7 @@ export function StoragePanel() {
)} )}
{hasOrphans && ( {hasOrphans && (
<div className="flex gap-2"> <div className="flex flex-wrap gap-2">
<button <button
onClick={() => handleCleanup(true)} onClick={() => handleCleanup(true)}
disabled={cleaning} disabled={cleaning}
@@ -358,7 +360,7 @@ export function StoragePanel() {
<div className="text-xs text-txt-tertiary"> <div className="text-xs text-txt-tertiary">
Abandoned tus upload sessions in <code className="text-txt-secondary">.tus/</code> (paused/crashed without DELETE). Auto-expire runs every 24 hours; this lets you sweep proactively. Abandoned tus upload sessions in <code className="text-txt-secondary">.tus/</code> (paused/crashed without DELETE). Auto-expire runs every 24 hours; this lets you sweep proactively.
</div> </div>
<div className="flex items-center gap-3"> <div className="flex flex-wrap items-center gap-x-3 gap-y-2">
<label className="text-sm text-txt-secondary whitespace-nowrap">Max age (hours)</label> <label className="text-sm text-txt-secondary whitespace-nowrap">Max age (hours)</label>
<input <input
type="number" type="number"
@@ -375,7 +377,7 @@ export function StoragePanel() {
/> />
</div> </div>
<div className="flex gap-2"> <div className="flex flex-wrap gap-2">
<button <button
onClick={() => handleTusCleanup(true)} onClick={() => handleTusCleanup(true)}
disabled={tusCleaning || tusMaxAgeHours <= 0} disabled={tusCleaning || tusMaxAgeHours <= 0}
@@ -418,19 +420,21 @@ export function StoragePanel() {
<div> <div>
<div className="text-[11px] font-semibold text-txt-tertiary uppercase tracking-wider mb-1.5">Media Retention</div> <div className="text-[11px] font-semibold text-txt-tertiary uppercase tracking-wider mb-1.5">Media Retention</div>
<div className="rounded-lg bg-white/[0.02] p-3.5 space-y-3"> <div className="rounded-lg bg-white/[0.02] p-3.5 space-y-3">
<div className="flex items-center gap-3"> <div className="flex flex-wrap items-center gap-x-3 gap-y-2">
<label className="text-sm text-txt-secondary whitespace-nowrap">Delete chat media older than</label> <label className="text-sm text-txt-secondary whitespace-nowrap">Delete chat media older than</label>
<input <div className="flex items-center gap-3">
type="number" <input
min={1} type="number"
value={mediaAgeDays} min={1}
onChange={(e) => { setMediaAgeDays(Number(e.target.value)); setMediaPreviewDone(false); setMediaCleanupResult(null); }} value={mediaAgeDays}
className="input-standard w-20 px-2 py-1 text-sm text-center" onChange={(e) => { setMediaAgeDays(Number(e.target.value)); setMediaPreviewDone(false); setMediaCleanupResult(null); }}
/> className="input-standard w-20 px-2 py-1 text-sm text-center"
<span className="text-sm text-txt-tertiary">days</span> />
<span className="text-sm text-txt-tertiary">days</span>
</div>
</div> </div>
<div className="flex gap-2"> <div className="flex flex-wrap gap-2">
<button <button
onClick={() => handleMediaCleanup(true)} onClick={() => handleMediaCleanup(true)}
disabled={mediaCleaning || mediaAgeDays < 1} disabled={mediaCleaning || mediaAgeDays < 1}