atagen

malice

public
Star0
Watch0

Commit c1e86c4a3a6c

tarball

atagen <boss@atagen.co>2026-10-26 02:11 UTC

2 files changed+31-1
web: rail unread dots, fold rail outside rooms, keep account bar visible
Modified file: web/src/App.svelte+19 additions−1 deletions
@@ -204,6 +204,10 @@
204204 $: orderedRail = sortSpaces(railSpaces, railOverride);
205205 $: pinnedRail = orderedRail.filter((s) => s.favourite);
206206 $: otherRail = orderedRail.filter((s) => !s.favourite);
207+ $: unreadSpaces = spacesWithUnread($rooms, spaceParents);
208+ $: homeUnread =
209+ ($mentionsSnapshot?.unread_count ?? 0) > 0 ||
210+ $rooms.some((r) => r.direct && !r.space && r.membership === 'Joined' && roomUnread(r) > 0);
207211 $: railSelected = selectedSpaceId ? topSpaceOf(selectedSpaceId, spaceParents) : null;
208212 $: invites = $rooms.filter((r) => r.membership === 'Invited');
209213 $: query = search.trim().toLowerCase();
@@ -457,6 +461,18 @@
457461 return parents;
458462 }
459463
464+ function spacesWithUnread(all: RoomSummary[], parents: Map<string, string>): Set<string> {
465+ const spaces = all.filter((r) => r.space);
466+ const ids = new Set(spaces.map((s) => s.room_id));
467+ const out = new Set<string>();
468+ for (const room of all) {
469+ if (room.space || room.membership !== 'Joined' || roomUnread(room) === 0) continue;
470+ const owners = [...room.parent_spaces, ...spaces.filter((s) => s.children.includes(room.room_id)).map((s) => s.room_id)];
471+ for (const id of owners) if (ids.has(id)) out.add(topSpaceOf(id, parents));
472+ }
473+ return out;
474+ }
475+
460476 function topSpaceOf(id: string, parents: Map<string, string>): string {
461477 let at = id;
462478 while (parents.has(at)) at = parents.get(at)!;
@@ -1741,7 +1757,7 @@
17411757 class="app"
17421758 in:fade={{ duration: motionDuration(220) }}
17431759 class:nav-folded={$navFolded}
1744- class:rail-folded={$railFolded && selectedRoom}
1760+ class:rail-folded={$railFolded && (selectedRoom || mentionsOpen || searchOpen)}
17451761 class:members-open={membersShown}
17461762 class:thread-open={threadShown}
17471763 class:resizing
@@ -1902,6 +1918,7 @@
19021918 type="button"
19031919 class="rail-btn"
19041920 class:active={selectedSpaceId === null}
1921+ class:unread={homeUnread}
19051922 title="Home — all channels"
19061923 aria-label="Home — all channels"
19071924 aria-pressed={selectedSpaceId === null}
@@ -1921,6 +1938,7 @@
19211938 class:drop-before={dropTarget?.id === space.room_id && !dropTarget.after}
19221939 class:drop-after={dropTarget?.id === space.room_id && dropTarget.after}
19231940 class:pinned={space.favourite}
1941+ class:unread={unreadSpaces.has(space.room_id)}
19241942 title={space.name}
19251943 aria-label={`Show channels in space ${space.name}`}
19261944 aria-pressed={selectedSpaceId === space.room_id}
Modified file: web/src/app.css+12 additions
@@ -228,6 +228,7 @@ input, textarea { font: inherit; }
228228 .app > .rail { grid-column: 1; grid-row: 2; }
229229 .app > .sidebar { grid-column: 2; grid-row: 2; }
230230 .app > .account { grid-column: 1; grid-row: 3; }
231+@media (min-width: 641px) { .app.rail-folded > .account { grid-column: 2; } }
231232 .app > .chat { grid-column: 3; grid-row: 2 / 4; }
232233 .app > .thread { grid-column: 4; grid-row: 2 / 4; }
233234 .app > .members { grid-column: 5; grid-row: 2 / 4; }
@@ -279,6 +280,17 @@ input, textarea { font: inherit; }
279280 padding: 0;
280281 }
281282 .rail-btn:hover { background: var(--accent); color: var(--on-accent); }
283+.rail-btn.unread { position: relative; overflow: visible; }
284+.rail-btn.unread::after {
285+ content: '';
286+ position: absolute;
287+ top: -2px; right: -2px;
288+ width: 10px; height: 10px;
289+ border-radius: 50%;
290+ background: var(--danger);
291+ box-shadow: 0 0 0 2px var(--rail-bg);
292+ pointer-events: none;
293+}
282294 .rail-btn.active { background: var(--accent); color: var(--on-accent); }
283295 .rail-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
284296 .rail-space { border-radius: 50%; transition: border-radius var(--pop), background var(--pop), color var(--pop), box-shadow var(--pop); }