mirror your GitHub repos to tangled.org automatically
1

Configure Feed

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

perf: prerender skeleton loader on dashboard page

+94 -12
+2 -8
app/middleware/authenticated.ts
··· 1 1 export default defineNuxtRouteMiddleware(async () => { 2 - // On SSR we must forward the request cookies so the whoami probe sees the 3 - // session. On the client, same-origin `$fetch` already sends cookies. 4 - const headers: Record<string, string> = {} 5 - if (import.meta.server) { 6 - const cookie = useRequestHeader('cookie') 7 - if (cookie) headers.cookie = cookie 8 - } 2 + if (import.meta.server) return 9 3 10 4 try { 11 - await $fetch('/api/me/whoami', { headers }) 5 + await $fetch('/api/me/whoami') 12 6 } 13 7 catch (err: unknown) { 14 8 const status = err && typeof err === 'object'
+91 -3
app/pages/dashboard.vue
··· 14 14 description: 'Your synchub.to mirror status.', 15 15 }) 16 16 17 - const { data, refresh, error } = await useFetch<DashboardPayload>('/api/me/dashboard', { server: false }) 18 - const { data: accountsData, refresh: refreshAccounts } = await useFetch<AccountsPayload>('/api/me/accounts', { server: false }) 17 + const { data, refresh, error } = useFetch<DashboardPayload>('/api/me/dashboard', { 18 + server: false, 19 + lazy: true, 20 + }) 21 + const { data: accountsData, refresh: refreshAccounts } = useFetch<AccountsPayload>('/api/me/accounts', { 22 + server: false, 23 + lazy: true, 24 + }) 19 25 20 26 const switcher = useTemplateRef<HTMLDetailsElement>('switcher') 21 27 const switcherOpen = ref(false) ··· 250 256 <span aria-hidden="true">⚠</span> failed to load dashboard: {{ error.message }} 251 257 </div> 252 258 253 - <div v-else-if="data"> 259 + <div v-else-if="!data" class="skeleton" aria-hidden="true"> 260 + <section class="card"> 261 + <h2>identity</h2> 262 + <div class="sk sk--line" style="width: 60%" /> 263 + <div class="sk sk--line" style="width: 45%" /> 264 + </section> 265 + <section class="card"> 266 + <h2>SSH key</h2> 267 + <div class="sk sk--line" style="width: 35%" /> 268 + <div class="sk sk--block" /> 269 + <div class="sk sk--btn" /> 270 + </section> 271 + <section class="card"> 272 + <h2>repositories</h2> 273 + <div v-for="n in 2" :key="n" class="sk-repo"> 274 + <div class="sk-repo__main"> 275 + <div class="sk sk--line" style="width: 40%" /> 276 + <div class="sk sk--line sk--meta" style="width: 70%" /> 277 + </div> 278 + <div class="sk sk--btn" /> 279 + </div> 280 + </section> 281 + </div> 282 + 283 + <div v-else> 254 284 <section class="card"> 255 285 <h2>identity</h2> 256 286 <dl> ··· 555 585 .error { 556 586 border-color: var(--color-error); 557 587 color: var(--color-error); 588 + } 589 + 590 + /* Skeleton: dimensions mirror the loaded cards so swapping in real data 591 + doesn't shift layout. */ 592 + .sk { 593 + border-radius: var(--radius-sm); 594 + background: var(--color-paper-3); 595 + position: relative; 596 + overflow: hidden; 597 + } 598 + 599 + .sk::after { 600 + content: ""; 601 + position: absolute; 602 + inset: 0; 603 + transform: translateX(-100%); 604 + background: linear-gradient(90deg, transparent, color-mix(in oklch, var(--color-paper-2) 60%, transparent), transparent); 605 + animation: sk-shimmer 1.4s var(--ease-in-out) infinite; 606 + } 607 + 608 + @keyframes sk-shimmer { 609 + to { transform: translateX(100%); } 610 + } 611 + 612 + .sk--line { 613 + height: 1.1em; 614 + margin-block: var(--space-xs); 615 + } 616 + 617 + .sk--meta { 618 + height: 0.85em; 619 + } 620 + 621 + .sk--block { 622 + height: 3.2em; 623 + margin-block: var(--space-sm); 624 + } 625 + 626 + .sk--btn { 627 + height: 2.1em; 628 + width: 8rem; 629 + } 630 + 631 + .sk-repo { 632 + display: flex; 633 + align-items: start; 634 + justify-content: space-between; 635 + gap: var(--space-lg); 636 + padding-block: var(--space-md); 637 + border-top: var(--rule-hair) solid var(--color-rule); 638 + } 639 + 640 + .sk-repo:first-of-type { border-top: 0; } 641 + .sk-repo__main { flex: 1; min-width: 0; } 642 + .sk-repo .sk--btn { flex: none; width: 5.5rem; } 643 + 644 + @media (prefers-reduced-motion: reduce) { 645 + .sk::after { animation: none; } 558 646 } 559 647 560 648 dl {
+1 -1
nuxt.config.ts
··· 67 67 }, 68 68 routeRules: { 69 69 '/': { noScripts: true, prerender: true }, 70 - '/dashboard': { ssr: false, prerender: true }, 70 + '/dashboard': { prerender: true }, 71 71 '/connect': { noScripts: true }, 72 72 }, 73 73 nitro: {