csr tangled client in solid-js
15

Configure Feed

Select the types of activity you want to include in your feed.

improve user menu to match upstream more

dawn (May 30, 2026, 1:31 AM +0300) fef752a1 77dbcd16

+165 -35
+105
src/index.css
··· 3476 3476 grid-template-columns: auto 1fr auto; 3477 3477 } 3478 3478 3479 + 3480 + .untangled-topbar-menu { 3481 + position: relative; 3482 + } 3483 + 3484 + .untangled-topbar-menu-summary { 3485 + cursor: pointer; 3486 + list-style: none; 3487 + display: flex; 3488 + align-items: center; 3489 + gap: 0.5rem; 3490 + } 3491 + 3492 + .untangled-topbar-menu-summary::-webkit-details-marker { 3493 + display: none; 3494 + } 3495 + 3496 + .untangled-topbar-menu-dropdown { 3497 + position: absolute; 3498 + right: 0; 3499 + margin-top: 0.5rem; 3500 + border-radius: 0.375rem; 3501 + background-color: rgb(255 255 255); 3502 + border: 1px solid rgb(229 231 235); 3503 + box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); 3504 + z-index: 50; 3505 + font-size: 0.875rem; 3506 + width: 14rem; 3507 + overflow: hidden; 3508 + } 3509 + 3510 + @media (prefers-color-scheme: dark) { 3511 + .untangled-topbar-menu-dropdown { 3512 + background-color: rgb(31 41 55); 3513 + border-color: rgb(55 65 81); 3514 + } 3515 + } 3516 + 3517 + .untangled-topbar-menu-item { 3518 + display: flex; 3519 + width: 100%; 3520 + align-items: center; 3521 + gap: 0.75rem; 3522 + padding: 0.625rem 1rem; 3523 + text-align: left; 3524 + text-decoration: none; 3525 + color: inherit; 3526 + transition: background-color 150ms; 3527 + border: 0; 3528 + background: transparent; 3529 + cursor: pointer; 3530 + } 3531 + 3532 + .untangled-topbar-menu-item:hover { 3533 + background-color: rgb(243 244 246); 3534 + text-decoration: none; 3535 + } 3536 + 3537 + @media (prefers-color-scheme: dark) { 3538 + .untangled-topbar-menu-item:hover { 3539 + background-color: rgb(55 65 81 / 0.5); 3540 + } 3541 + } 3542 + 3543 + .untangled-topbar-menu-item-danger { 3544 + color: rgb(239 68 68); 3545 + } 3546 + 3547 + .untangled-topbar-menu-item-danger:hover { 3548 + background-color: rgb(254 242 242); 3549 + } 3550 + 3551 + @media (prefers-color-scheme: dark) { 3552 + .untangled-topbar-menu-item-danger:hover { 3553 + background-color: rgb(153 27 27 / 0.2); 3554 + } 3555 + } 3556 + 3557 + .untangled-topbar-menu-add-account-icon { 3558 + display: flex; 3559 + align-items: center; 3560 + justify-content: center; 3561 + width: 1.5rem; 3562 + height: 1.5rem; 3563 + border-radius: 9999px; 3564 + background-color: rgb(229 231 235); 3565 + color: rgb(55 65 81); 3566 + } 3567 + 3568 + @media (prefers-color-scheme: dark) { 3569 + .untangled-topbar-menu-add-account-icon { 3570 + background-color: rgb(75 85 99); 3571 + color: rgb(229 231 235); 3572 + } 3573 + } 3574 + 3575 + .untangled-topbar-menu-item-separator { 3576 + border-top: 1px solid rgb(229 231 235); 3577 + } 3578 + 3579 + @media (prefers-color-scheme: dark) { 3580 + .untangled-topbar-menu-item-separator { 3581 + border-color: rgb(55 65 81); 3582 + } 3583 + }
+60 -35
src/layout.tsx
··· 1 1 import clsx from 'clsx'; 2 - import { Bell, LogOut, RotateCcw, Save, Search, Settings, UserRound } from 'lucide-solid'; 2 + import { Bell, BookMarked, LogOut, Plus, RotateCcw, Save, Search, Settings, UserRound } from 'lucide-solid'; 3 3 import { A, useLocation, useNavigate } from '@solidjs/router'; 4 4 import { createQuery, useQueryClient } from '@tanstack/solid-query'; 5 5 import { For, Show, createEffect, createSignal, onCleanup, onMount, type Component, type JSX } from 'solid-js'; ··· 218 218 queryFn: async () => resolveActor(auth.currentDid()!), 219 219 })); 220 220 221 + const closeMenu = (e: MouseEvent) => { 222 + const details = (e.currentTarget as HTMLElement).closest('details'); 223 + if (details) details.open = false; 224 + }; 225 + 221 226 return ( 222 - <details data-topbar-menu class="relative"> 223 - <summary class="cursor-pointer list-none flex items-center gap-2"> 227 + <details data-topbar-menu class="untangled-topbar-menu"> 228 + <summary class="untangled-topbar-menu-summary"> 224 229 <Avatar did={auth.currentDid()!} size="size-8 md:size-6" /> 225 230 <span class="hidden md:inline">{viewerQuery.data?.handle ?? auth.currentDid()}</span> 226 231 </summary> 227 - <div class="absolute right-0 mt-4 rounded bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-lg z-50 text-sm w-56"> 228 - <div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700"> 229 - <div class="font-medium truncate">{viewerQuery.data?.handle ?? auth.currentDid()}</div> 230 - <div class="text-gray-500 dark:text-gray-400 truncate">{auth.currentDid()}</div> 232 + <div class="untangled-topbar-menu-dropdown"> 233 + <button type="button" class="untangled-topbar-menu-item" onClick={() => void auth.signIn()}> 234 + <div class="untangled-topbar-menu-add-account-icon"> 235 + <Plus class="size-4" /> 236 + </div> 237 + add account 238 + </button> 239 + 240 + <div class="untangled-topbar-menu-item-separator"> 241 + <Show when={viewerQuery.data?.handle}> 242 + {(handle) => ( 243 + <> 244 + <A href={`/${handle()}`} class="untangled-topbar-menu-item" onClick={closeMenu}> 245 + <UserRound class="size-4" /> 246 + profile 247 + </A> 248 + <A href={`/${handle()}?tab=repos`} class="untangled-topbar-menu-item" onClick={closeMenu}> 249 + <BookMarked class="size-4" /> 250 + repositories 251 + </A> 252 + </> 253 + )} 254 + </Show> 255 + 256 + <A href="/settings" class="untangled-topbar-menu-item" onClick={closeMenu}> 257 + <Settings class="size-4" /> 258 + settings 259 + </A> 260 + 261 + <button 262 + type="button" 263 + class="untangled-topbar-menu-item untangled-topbar-menu-item-danger" 264 + onClick={() => void auth.signOut()} 265 + > 266 + <LogOut class="size-4" /> 267 + logout 268 + </button> 231 269 </div> 232 - <Show when={viewerQuery.data?.handle}> 233 - {(handle) => ( 234 - <A 235 - href={`/${handle()}`} 236 - class="w-full px-4 py-3 flex items-center gap-2 text-left hover:bg-gray-50 hover:dark:bg-gray-700/50 text-black dark:text-white no-underline hover:no-underline border-b border-gray-200 dark:border-gray-700" 237 - onClick={(e) => { 238 - const details = e.currentTarget.closest('details'); 239 - if (details) details.open = false; 240 - }} 241 - > 242 - <UserRound class="size-4" /> 243 - profile 244 - </A> 245 - )} 246 - </Show> 247 - <button 248 - type="button" 249 - class="w-full px-4 py-3 flex items-center gap-2 text-left hover:bg-gray-50 hover:dark:bg-gray-700/50" 250 - onClick={() => void auth.signOut()} 251 - > 252 - <LogOut class="size-4" /> 253 - logout 254 - </button> 255 270 </div> 256 271 </details> 257 272 ); ··· 335 350 return ( 336 351 <nav ref={(element) => { topbar = element; }} class="mx-auto space-x-4 px-6 py-2"> 337 352 <div class="flex justify-between gap-4 p-0 items-center"> 338 - <A href="/" class="text-2xl no-underline hover:no-underline flex shrink-0 items-center gap-2"> 339 - <img src="/static/logos/dolly.svg" alt="" class="size-8" /> 340 - <span class="font-bold text-xl">untangled</span> 341 - <span class="font-normal text-xs rounded bg-gray-100 dark:bg-gray-700 px-1">alpha</span> 342 - </A> 353 + <div class="flex shrink-0 items-center gap-2"> 354 + <A href="/" class="flex shrink-0 items-center gap-2 no-underline hover:no-underline"> 355 + <img src="/static/logos/dolly.svg" alt="" class="size-8" /> 356 + <div class="flex flex-col"> 357 + <span class="font-bold text-xl leading-none">untangled</span> 358 + <a 359 + href="https://untangled.wisp.place/ptr.pet/untangled" 360 + class="text-[10px] leading-none text-gray-400 dark:text-gray-500 hover:text-blue-500 dark:hover:text-blue-400 no-underline transition-colors mt-0.5" 361 + onClick={(e) => e.stopPropagation()} 362 + > 363 + src 364 + </a> 365 + </div> 366 + </A> 367 + </div> 343 368 344 369 <div class="flex shrink-0 items-center gap-4"> 345 370 <TopbarSearch />