setup: put an icon in the slot that was reserving room for one

The storage and destination cards carry a 20px .setup-storage-spacer so they
line up with the app cards, which have icons — so it held space for a picture
and then showed nothing, leaving a gap that reads as something failing to load.

Fill it: a drive for local, a server for SFTP, a cloud for S3/B2, and two linked
nodes for a peer. Inline SVG rather than assets, stroked in currentColor so they
follow the theme and pick up the accent with the rest of the card on hover.

Also drop the standing note under Destinations ("Everything here is on this
machine…"). Each card already says where it points, and the one that shares a
disk with the app data says so on the card itself; a paragraph that never
changes is furniture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-28 11:41:26 +01:00
parent d51e014cad
commit f227435abf
2 changed files with 36 additions and 8 deletions

View File

@ -1372,6 +1372,16 @@ body.setup-wizard-open .custom-select-popup { z-index: 10001; }
flex-shrink: 0;
}
/* Fills that same slot. Muted by default so the row reads name-first, and it
picks up the accent with the rest of the card on hover. */
.setup-storage-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
color: rgba(var(--text-rgb), 0.55);
}
.setup-storage-card:hover .setup-storage-icon { color: var(--accent); }
/* Divider between "where apps go regardless" and "what you can opt into". */
.setup-storage-divider {
display: flex;

View File

@ -570,6 +570,27 @@ class SetupWizard {
return s.mount && s.mount !== '/' ? s.mount : (s.path || 'Default location');
}
// The 20px slot in these cards exists to line them up with the app cards,
// which carry an icon — so it was reserving room for a picture and then
// showing nothing. Inline SVG rather than an asset: one glyph each, stroked
// in currentColor so it follows the theme.
_cardIcon(kind) {
const svg = (d) => `<svg class="setup-storage-icon" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"
aria-hidden="true">${d}</svg>`;
switch (kind) {
case 'cloud': // S3, B2 — someone else's disk
return svg('<path d="M17.5 19a4.5 4.5 0 0 0 .5-8.97 6 6 0 0 0-11.66-1.4A3.75 3.75 0 0 0 6.5 19z"/>');
case 'server': // SFTP — a machine you can name
return svg('<rect x="3" y="4" width="18" height="7" rx="1.5"/><rect x="3" y="13" width="18" height="7" rx="1.5"/><path d="M7 7.5h.01M7 16.5h.01"/>');
case 'peer': // another LibrePortal
return svg('<circle cx="7" cy="12" r="2.6"/><circle cx="17" cy="12" r="2.6"/><path d="M9.6 12h4.8"/>');
case 'disk':
default: // a local drive
return svg('<rect x="3" y="5" width="18" height="14" rx="2"/><path d="M7 9.5h10M7 14.5h6"/><circle cx="17.2" cy="14.5" r="1"/>');
}
}
_storageCard(c, key, opts) {
const o = opts || {};
const refused = c.verdict === 'refuse';
@ -582,7 +603,7 @@ class SetupWizard {
return `
<div class="setup-app setup-storage-card${refused ? ' setup-storage-disabled' : ''} setup-storage-locked">
<span class="setup-storage-spacer" aria-hidden="true"></span>
${this._cardIcon('disk')}
<span class="setup-app-body">
<span class="setup-app-name">${this.escapeHtml(title)} ${badge}</span>
<span class="setup-app-desc">
@ -797,7 +818,7 @@ class SetupWizard {
: `${this.escapeHtml(l.type.toUpperCase())} \u00b7 ${this.escapeHtml(l.ssh_host || l.host || '')}`;
return `
<div class="setup-app setup-storage-card setup-storage-locked">
<span class="setup-storage-spacer" aria-hidden="true"></span>
${this._cardIcon(l.type === 'local' ? 'disk' : (l.type === 'sftp' ? 'server' : (l.type === 'peer' ? 'peer' : 'cloud')))}
<span class="setup-app-body">
<span class="setup-app-name">${this.escapeHtml(l.name)} ${badge} ${warn}</span>
<span class="setup-app-desc">${this.escapeHtml(where)}</span>
@ -815,12 +836,9 @@ class SetupWizard {
const add = box.querySelector('#sw-backup-add');
if (add) add.addEventListener('click', () => this.showBackupDestModal(-1));
if (note) {
const offsite = this.backupLocations.some(l => l.type !== 'local');
note.innerHTML = offsite
? ''
: 'Everything here is on this machine. That protects against deletions and bad updates \u2014 but not against losing the machine. Add an SFTP or S3 destination for that.';
}
// No standing note here. The cards already say where each destination is,
// and a paragraph that never changes is furniture.
if (note) note.innerHTML = '';
}
// Add or edit a destination. Local wants a path; everything else wants