Compare commits

..

No commits in common. "3c8963daf8ff924d92525d1b15db5cc910395f00" and "f30fe49548e5ef23fb565f77be68e7fc146b910f" have entirely different histories.

2 changed files with 39 additions and 20 deletions

View File

@ -6,25 +6,23 @@
--app-min: 300px; --app-min: 300px;
--app-gap: 20px; --app-gap: 20px;
display: grid; display: grid;
/* Fixed-width tracks so cards stay exactly --app-min wide regardless /* JS sets --app-cols to a balanced track count: as many as fit the
of how many are visible a 2-card category lines up with a 3-card row at min-width, but reduced by 1 if the natural count would
category at the same X positions. auto-fill packs as many tracks orphan a single card on the last row (e.g. 4 cards on a 3-col
as the row fits at the current viewport. The partial-row orphan viewport becomes a 2x2 grid instead of 3+1). Falls back to
(e.g. 4 cards on a 3-col viewport 3+1 with the 4th alone on row auto-fill while JS hasn't run yet. Cards stretch via 1fr so the
2) is an accepted tradeoff: we tried rebalancing to 2x2, but that last row never leaves a card-shaped hole accepting some width
dropped the cards-per-row count too aggressively for larger variation across categories in exchange for no internal gaps. */
viewports keeping rows dense beats avoiding the orphan. */ grid-template-columns: repeat(var(--app-cols, auto-fill), minmax(var(--app-min), 1fr));
grid-template-columns: repeat(auto-fill, var(--app-min));
gap: var(--app-gap); gap: var(--app-gap);
margin: 22px; margin: 22px;
padding: 22px; padding: 22px;
background: rgba(var(--text-rgb), 0.025); background: rgba(var(--text-rgb), 0.025);
border: 1px solid var(--border-subtle); border: 1px solid var(--border-subtle);
border-radius: 16px; border-radius: 16px;
/* Box reaches the layout edge when the visible cards already fill a /* Box reaches the layout edge for 2+ visible cards (sentinel from
natural row (sentinel from apps-manager.js sets --app-count to 99); apps-manager.js); for a single card the formula caps the box to
for fewer cards the formula caps the box to exactly that many so one card's worth instead of stretching it across the full row.
no card-shaped hole sits to the right of a small category.
Outer under border-box (global, style.css:4): N*min + (N-1)*gap + Outer under border-box (global, style.css:4): N*min + (N-1)*gap +
44px padding + 2px border + 2px sub-pixel buffer. */ 44px padding + 2px border + 2px sub-pixel buffer. */
max-width: min( max-width: min(

View File

@ -552,10 +552,20 @@ class AppsManager {
}); });
} }
// Sync --app-count on .apps-section so the CSS max-width cap shrinks // Sync --app-cols + --app-count on .apps-section so the grid lays out
// the box around a few visible cards (no card-shaped hole on the // without orphans on the last row.
// right for a half-empty row) but disengages when visible cards meet //
// the natural full-width column count (box reaches the parent edge). // --app-cols picks a balanced column count: as many as the row fits
// naturally, but reduced by 1 when the natural choice would leave a
// single card alone on the last row (e.g. 4 cards on a 3-col viewport
// becomes 2x2 instead of 3+1). 5+2, 6+3, etc are accepted — only the
// worst case (last row has exactly 1 orphan) is rebalanced.
//
// --app-count drives the CSS max-width cap: 1 visible card shrinks
// the box to one card's worth (stretching one card to 1000px+ looks
// bad); 2+ cards pass a sentinel (99) so the cap defers to the
// 100%-44px parent and the box runs edge-to-edge.
//
// Driven from render, sidebar search filter, and window resize. // Driven from render, sidebar search filter, and window resize.
updateAppsCount() { updateAppsCount() {
const container = document.getElementById('apps-section'); const container = document.getElementById('apps-section');
@ -573,11 +583,22 @@ class AppsManager {
// 22px margin + 22px padding + 1px border, doubled. // 22px margin + 22px padding + 1px border, doubled.
const parent = container.parentElement; const parent = container.parentElement;
const inside = parent ? Math.max(0, parent.clientWidth - 90) : 0; const inside = parent ? Math.max(0, parent.clientWidth - 90) : 0;
const naturalCols = inside > 0 const maxCols = inside > 0
? Math.max(1, Math.floor((inside + gap) / (minCol + gap))) ? Math.max(1, Math.floor((inside + gap) / (minCol + gap)))
: visible; : visible;
const effective = visible >= naturalCols ? 99 : visible;
container.style.setProperty('--app-count', effective); let cols = Math.min(visible, maxCols);
// Avoid orphaning a single card on the last row — reduce columns
// by one so the layout becomes (cols-1)+(cols-1)+… with a fuller
// tail. Don't drop below 2; a 1-col stack looks worse than an
// orphan. Only handles the immediate "last row is 1" case;
// last-row-of-2 etc are accepted as good enough.
if (cols > 2 && visible > cols && visible % cols === 1) {
cols--;
}
container.style.setProperty('--app-cols', cols);
container.style.setProperty('--app-count', visible === 1 ? 1 : 99);
} }
// Client-side substring filter wired to the sidebar search box. // Client-side substring filter wired to the sidebar search box.