Theseus 0.3.93: home — slot area widened to a 3-column grid

The left column grows from a single narrow stack to a 3-wide grid of slots
so added icons flow sideways instead of pushing the page tall. The "+" add
button sits in the next free cell of that same grid. Drag-reorder on the
left column switches from above/below to before/after indicators to match
the right-side cards; both sections are 2D grids now.
This commit is contained in:
Local Dev 2026-10-20 23:04:43 +03:00
parent 092a779a6a
commit 4e865a6286
2 changed files with 38 additions and 37 deletions

View file

@ -28,15 +28,19 @@
.hero form button { padding: 13px 20px; border-radius: 999px; border: none; background: var(--blue); color: #fff; font-size: 15px; cursor: pointer; }
.hero .hint { color: var(--dim); font-size: 12.5px; margin-top: .8rem; }
/* Two-column desktop under the hero: narrow LEFT column of stackable icon
slots, wider RIGHT column with a 3-column grid of Silent Mode cards. */
.homepage { display: grid; grid-template-columns: minmax(96px, 110px) minmax(380px, 1fr);
gap: 36px; max-width: 1180px; margin: 2.4rem auto 0; padding: 0 32px 48px; align-items: start; }
@media (max-width: 760px) { .homepage { grid-template-columns: 1fr; gap: 20px; padding: 0 14px 32px; } }
/* Two-column desktop under the hero: wide LEFT area for a 3-column grid
of icon slots, wider RIGHT area with a 3-column grid of Silent Mode
cards. Both expand together on wider screens. */
.homepage { display: grid; grid-template-columns: minmax(300px, 340px) minmax(380px, 1fr);
gap: 36px; max-width: 1400px; margin: 2.4rem auto 0; padding: 0 32px 48px; align-items: start; }
@media (max-width: 900px) { .homepage { grid-template-columns: 1fr; gap: 20px; padding: 0 14px 32px; } }
/* LEFT COLUMN — vertical stack of slots + an "add" button underneath */
.left-column { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.slots-list { display: flex; flex-direction: column; gap: 14px; align-items: center; width: 100%; }
/* LEFT AREA — 3-column grid of slots, with the "add" button flowing in
the grid alongside them. Icons fill left-to-right, top-to-bottom; the
container is wide enough that a few icons still read as a tidy row. */
.left-column { display: flex; flex-direction: column; gap: 14px; }
.slots-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; width: 100%; }
@media (max-width: 900px) { .slots-list { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); } }
.slot { position: relative; display: flex; flex-direction: column; align-items: center;
cursor: pointer; user-select: none; text-decoration: none; color: inherit;
@ -58,18 +62,21 @@
.slot.size-large .tile { width: 88px; height: 88px; font-size: 34px; border-radius: 18px; }
.slot.size-small .label { font-size: 10.5px; max-width: 72px; }
.slot.dragging { opacity: .6; }
.slot.drag-above::before, .slot.drag-below::after {
content: ""; position: absolute; left: 50%; transform: translateX(-50%);
width: 60%; height: 2px; background: var(--acid); border-radius: 2px;
box-shadow: 0 0 8px var(--acid);
.slot.drag-before::before, .slot.drag-after::after {
content: ""; position: absolute; top: 10%; bottom: 10%; width: 3px;
background: var(--acid); border-radius: 2px; box-shadow: 0 0 8px var(--acid);
}
.slot.drag-above::before { top: -8px; }
.slot.drag-below::after { bottom: -8px; }
.slot.drag-before::before { left: -8px; }
.slot.drag-after::after { right: -8px; }
/* Slot-add sits in the grid alongside the slot icons so it's always on
the next free cell, not pinned under the whole column. */
.slot-add { display: flex; align-items: center; justify-content: center;
width: 56px; height: 56px; border-radius: 12px; border: 2px dashed var(--line2);
min-height: 72px; border-radius: 12px; border: 2px dashed var(--line2);
background: transparent; color: var(--mut); font-size: 22px; cursor: pointer;
transition: color .12s, border-color .12s, background .12s; }
transition: color .12s, border-color .12s, background .12s;
margin-top: 15px; /* visually below the label of the icon above */ }
.slots-list .slot-add { margin-top: 0; } /* in-grid, no extra top gap */
.slot-add:hover { color: var(--acid); border-color: var(--acid); background: rgb(from var(--acid) r g b / .04); }
/* RIGHT COLUMN — just the 3-column grid of Silent Mode cards; search
@ -190,7 +197,6 @@
<div class="homepage">
<div class="left-column">
<div class="slots-list" id="slotsList"></div>
<button class="slot-add" id="slotAdd" title="Add link">+</button>
</div>
<div class="right-column">
<div class="cards-grid" id="cardsGrid"></div>
@ -399,6 +405,11 @@
wireItem(el, "q", i);
list.appendChild(el);
});
const slotAdd = document.createElement("button");
slotAdd.className = "slot-add"; slotAdd.textContent = "+"; slotAdd.title = "Add link";
slotAdd.onclick = (e) => { e.preventDefault(); e.stopPropagation(); openModal("q", -1); };
slotAdd.oncontextmenu = (e) => e.preventDefault();
list.appendChild(slotAdd);
const sm = state.sections.sm || [];
const grid = $("cardsGrid");
@ -448,34 +459,26 @@
});
el.addEventListener("dragend", () => {
el.classList.remove("dragging");
document.querySelectorAll(".drag-above,.drag-below,.drag-before,.drag-after")
.forEach((n) => n.classList.remove("drag-above", "drag-below", "drag-before", "drag-after"));
document.querySelectorAll(".drag-before,.drag-after")
.forEach((n) => n.classList.remove("drag-before", "drag-after"));
});
el.addEventListener("dragover", (e) => {
e.preventDefault(); e.dataTransfer.dropEffect = "move";
const r = el.getBoundingClientRect();
// Vertical mid-split for slots, horizontal mid-split for cards.
if (sec === "q") {
const above = e.clientY < r.top + r.height / 2;
el.classList.toggle("drag-above", above);
el.classList.toggle("drag-below", !above);
} else {
const before = e.clientX < r.left + r.width / 2;
el.classList.toggle("drag-before", before);
el.classList.toggle("drag-after", !before);
}
// Both sections are 2D grids now — horizontal mid-split picks insert side.
const before = e.clientX < r.left + r.width / 2;
el.classList.toggle("drag-before", before);
el.classList.toggle("drag-after", !before);
});
el.addEventListener("dragleave", () => {
el.classList.remove("drag-above", "drag-below", "drag-before", "drag-after");
el.classList.remove("drag-before", "drag-after");
});
el.addEventListener("drop", (e) => {
e.preventDefault();
const payload = tryParse(e.dataTransfer.getData("application/x-slot"));
if (!payload) return;
const r = el.getBoundingClientRect();
const side = (sec === "q")
? (e.clientY < r.top + r.height / 2 ? -1 : 1)
: (e.clientX < r.left + r.width / 2 ? -1 : 1);
const side = e.clientX < r.left + r.width / 2 ? -1 : 1;
let targetIdx = i + (side === 1 ? 1 : 0);
moveItem(payload.sec, payload.i, sec, targetIdx);
});
@ -695,9 +698,7 @@
r.readAsDataURL(f);
};
// Slot-add button (bottom of the left column) and search form
$("slotAdd").onclick = (e) => { e.preventDefault(); openModal("q", -1); };
$("slotAdd").oncontextmenu = (e) => e.preventDefault();
// Search form (slot-add button is rendered inside slotsList per render())
$("searchForm").addEventListener("submit", (e) => {
e.preventDefault();
const q = $("q").value.trim(); if (!q) return;

View file

@ -1,6 +1,6 @@
{
"name": "theseus-navigator",
"version": "0.3.92",
"version": "0.3.93",
"license": "MPL-2.0",
"description": "Theseus Navigator — a browser that follows the thread. By Silent Mode, a Deviant project.",
"author": "Silent Mode",