From e36bc09408b26cfd308fda24e7115b06bce32a4c Mon Sep 17 00:00:00 2001 From: Salastil Date: Sun, 23 Aug 2026 21:21:32 -0400 Subject: [PATCH] Draw an expanded folder as a panel around its servers An open folder and the servers inside it now share a container with a hairline border and a ground a shade lighter than the rail, the way Discord marks one. Without it there was nothing to say where the folder ended - the contents are deliberately not indented, so a run of tiles below a folder looked exactly like the rest of the column. This needed the members to belong to the folder rather than being spliced into the entry list beside it, which is a better shape anyway: the folder now carries what is in it and the rail decides whether to draw them. Collapsed, the container has no border, no ground and no padding - the folder is just another tile in the column. Co-Authored-By: Claude Opus 5 --- src/renderer/src/app.css | 19 ++++++ src/renderer/src/components/ServerRail.tsx | 70 ++++++++++++---------- src/renderer/src/lib/groups.ts | 13 ++-- 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/renderer/src/app.css b/src/renderer/src/app.css index cca5d4e..05aac9e 100644 --- a/src/renderer/src/app.css +++ b/src/renderer/src/app.css @@ -2338,6 +2338,25 @@ video.lightbox-media { /* --- rail folders ------------------------------------------------------ */ +/* An expanded folder and its servers share one panel: a hairline border and + a ground a shade lighter than the rail, which is what marks where the + folder ends now that its contents are not indented. Collapsed, the + container is invisible and the folder is just another tile. */ +.rail-folder-group { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--spacing-xs); + width: 100%; +} + +.rail-folder-group.open { + padding: var(--spacing-xxs) 0; + background: var(--surface-container-low); + border: 1px solid var(--outline-medium); + border-radius: var(--corner-radius); +} + /* Squarer than a server tile, so a folder is not mistaken for one. */ .rail-folder .rail-face { border-radius: var(--corner-radius); diff --git a/src/renderer/src/components/ServerRail.tsx b/src/renderer/src/components/ServerRail.tsx index de2c61e..2898614 100644 --- a/src/renderer/src/components/ServerRail.tsx +++ b/src/renderer/src/components/ServerRail.tsx @@ -269,7 +269,38 @@ export function ServerRail(): JSX.Element | null { setNaming({ id, name: 'New folder' }) } - const entries = railEntries(ordered, folders, isFolderOpen) + const entries = railEntries(ordered, folders) + + const renderTile = (g: RailGroup): JSX.Element => { + const t = totals.get(g.id) + return ( + + setMutedGroups( + mutedGroups.includes(g.id) + ? mutedGroups.filter((x) => x !== g.id) + : [...mutedGroups, g.id] + ) + } + active={g.id === activeGroupId} + unread={t?.unread ?? 0} + highlight={t?.highlight ?? false} + // Direct messages and pinned lead the rail by definition, so + // there is nowhere for them to be dragged to. + draggable={!isFixedEntry(g)} + dropTarget={over === g.id && dragging !== '' && dragging !== g.id} + onSelect={() => store.selectGroup(g.id)} + onDragStart={() => beginDrag(g.id)} + onDragOver={() => !isFixedEntry(g) && setOver(g.id)} + onDrop={() => onDrop(g.id)} + onDragEnd={endDrag} + /> + ) + } return (