[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

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

Merge pull request #76 from flo-bit/alpha-v1

fix og images, complete og images for individual reviews

authored by

Florian and committed by
GitHub
(Dec 22, 2024, 11:42 PM +0100) 65d1df55 f857a439

+273 -116
-15
src/app.html
··· 15 15 data-website-id="a372b43f-99c8-4f8e-a1bf-0ea1f726df8a" 16 16 data-domains="skywatched.app" 17 17 ></script> 18 - 19 - <meta name="description" content="review movies and shows with your bluesky friends." /> 20 - 21 - <meta property="og:url" content="https://skywatched.app" /> 22 - <meta property="og:type" content="website" /> 23 - <meta property="og:title" content="skywatched" /> 24 - <meta property="og:description" content="review movies and shows with your bluesky friends." /> 25 - <meta property="og:image" content="/og.jpg" /> 26 - 27 - <meta name="twitter:card" content="summary_large_image" /> 28 - <meta property="twitter:domain" content="skywatched.app" /> 29 - <meta property="twitter:url" content="https://skywatched.app" /> 30 - <meta name="twitter:title" content="skywatched" /> 31 - <meta name="twitter:description" content="review movies and shows with your bluesky friends." /> 32 - <meta name="twitter:image" content="/og.jpg" /> 33 18 </head> 34 19 <body data-sveltekit-preload-data="hover" class="h-full bg-black text-white"> 35 20 <div style="display: contents">%sveltekit.body%</div>
+16
src/lib/Components/BaseHeadTags.svelte
··· 1 + <svelte:head> 2 + <meta name="description" content="review movies and shows with your bluesky friends." /> 3 + 4 + <meta property="og:url" content="https://skywatched.app" /> 5 + <meta property="og:type" content="website" /> 6 + <meta property="og:title" content="skywatched" /> 7 + <meta property="og:description" content="review movies and shows with your bluesky friends." /> 8 + <meta property="og:image" content="/og.jpg" /> 9 + 10 + <meta name="twitter:card" content="summary_large_image" /> 11 + <meta property="twitter:domain" content="skywatched.app" /> 12 + <meta property="twitter:url" content="https://skywatched.app" /> 13 + <meta name="twitter:title" content="skywatched" /> 14 + <meta name="twitter:description" content="review movies and shows with your bluesky friends." /> 15 + <meta name="twitter:image" content="/og.jpg" /> 16 + </svelte:head>
+3 -1
src/lib/Components/ItemCard.svelte
··· 86 86 <div class="mt-2 flex justify-between"> 87 87 <h3 class="sm:text-md text-sm font-medium text-base-50"> 88 88 <a 89 - href="/{item.movieId ? 'movie' : 'tv'}/{item.movieId ?? item.showId}-{nameToId(item.title ?? item.name ?? '')}" 89 + href="/{item.movieId ? 'movie' : 'tv'}/{item.movieId ?? item.showId}-{nameToId( 90 + item.title ?? item.name ?? '' 91 + )}" 90 92 > 91 93 <span aria-hidden="true" class="absolute inset-0"></span> 92 94 <div class="line-clamp-2 max-w-full">
+7 -4
src/lib/Components/ReviewCard.svelte
··· 35 35 <div class="flex flex-col gap-3"> 36 36 <a 37 37 href={`/user/${data.author.handle}`} 38 - class="flex flex-row items-center gap-4 overflow-hidden font-medium" 38 + class="z-20 flex flex-row items-center gap-4 overflow-hidden font-medium" 39 39 > 40 40 <div class="flex items-center gap-2"> 41 41 {#if data.author.avatar} ··· 56 56 href={data.record.item.ref === 'tmdb:m' 57 57 ? `/movie/${data.record.item.value}-${nameToId(data.record.metadata?.title ?? '')}` 58 58 : `/tv/${data.record.item.value}-${nameToId(data.record.metadata?.title ?? '')}`} 59 - class="title text-2xl font-bold hover:text-accent-200" 59 + class="title z-20 text-2xl font-bold hover:text-accent-200" 60 60 style:--name={`title-${data.record.item.value}`} 61 61 > 62 62 {data.record.metadata?.title} 63 63 </a> 64 64 {/if} 65 65 {#if data.record.rating?.value} 66 - <Rating rating={data.record.rating?.value / 2} size={'size-6'} /> 66 + <a href={`/review/${encodeURIComponent(data.uri)}`}> 67 + <Rating rating={data.record.rating?.value / 2} size={'size-6'} /> 68 + <span class="sr-only">View review</span> 69 + </a> 67 70 {/if} 68 71 </div> 69 72 </div> ··· 155 158 </div> 156 159 157 160 <!-- <a href={`/review/${encodeURIComponent(data.uri)}`}> 158 - <span class="absolute inset-0 z-10"></span> 161 + <span class="absolute inset-0 z-10 hover:bg-accent-500/10"></span> 159 162 </a> --> 160 163 </div>
+38 -10
src/lib/Components/Sidebar.svelte
··· 56 56 <div 57 57 class={cn( 58 58 'sidebar fixed bottom-2 left-0 top-2 z-50 w-[4.5rem] py-2 transition-transform duration-300', 59 - showSidebar.value ? 'translate-x-0' : '-translate-x-20 md:translate-x-0' 59 + showSidebar.value ? 'translate-x-0' : '-translate-x-64 md:translate-x-0' 60 60 )} 61 61 > 62 - <ul role="list" class="flex h-full flex-col items-center justify-between space-y-1 pb-2"> 62 + <ul 63 + role="list" 64 + class="flex h-full flex-col items-center justify-end space-y-1 pb-2 md:justify-between" 65 + > 63 66 <div class="flex flex-col items-center space-y-2"> 64 67 {#each menu as item} 65 - <li> 68 + <li class="group relative size-12"> 66 69 <a 67 70 href={item.href} 68 71 class={cn( 69 - 'group flex gap-x-3 rounded-md p-3 text-sm/6 font-semibold', 72 + 'group flex items-center gap-x-3 rounded-md p-3 text-sm/6 font-semibold', 70 73 $page.url.pathname === item.href 71 74 ? 'bg-accent-950/10 text-accent-400' 72 75 : 'text-base-200 transition-colors duration-100 hover:bg-accent-950/20 hover:text-accent-400' 73 76 )} 74 77 > 75 78 {@html item.icon} 76 - <span class="sr-only">{item.label}</span> 79 + <span 80 + class="absolute left-14 rounded-lg backdrop-blur-md transition-opacity duration-200 group-hover:opacity-100 md:bg-accent-950/20 md:px-3 md:py-2 md:opacity-0" 81 + >{item.label}</span 82 + > 77 83 </a> 78 84 </li> 79 85 {/each} ··· 81 87 <div class="flex flex-col items-center space-y-2"> 82 88 {#if user} 83 89 {#each userMenu as item} 84 - <li> 90 + <li class="group relative size-12"> 85 91 <a 86 92 href={item.href} 87 - class={'group flex gap-x-3 rounded-md p-3 text-sm/6 font-semibold text-base-200 transition-colors duration-100 hover:bg-accent-950/20 hover:text-accent-400'} 93 + class={'group flex items-center gap-x-3 rounded-md p-3 text-sm/6 font-semibold text-base-200 transition-colors duration-100 hover:bg-accent-950/20 hover:text-accent-400'} 88 94 onclick={(event) => { 89 95 event.preventDefault(); 90 96 item.onclick(); 91 97 }} 92 98 > 93 99 {@html item.icon} 94 - <span class="sr-only">{item.label}</span> 100 + <span 101 + class="absolute left-14 w-[6.5rem] rounded-lg backdrop-blur-md transition-opacity duration-200 group-hover:opacity-100 md:bg-accent-950/20 md:px-3 md:py-2 md:opacity-0" 102 + >{item.label}</span 103 + > 95 104 </a> 96 105 </li> 97 106 {/each} ··· 106 115 {#if showSidebar.value} 107 116 <button 108 117 transition:fade 109 - class="fixed inset-0 z-40 bg-base-900/50 backdrop-blur-sm md:hidden" 118 + class="fixed inset-0 z-40 bg-base-950/90 backdrop-blur-sm md:hidden" 110 119 onclick={() => showSidebar.toggle()} 111 120 > 112 121 <span class="sr-only">Close Menu</span> 113 122 </button> 123 + 124 + <button 125 + transition:fade 126 + onclick={() => showSidebar.toggle()} 127 + class="fixed bottom-6 right-4 z-50 md:hidden" 128 + > 129 + <span class="sr-only">close Menu</span> 130 + 131 + <svg 132 + xmlns="http://www.w3.org/2000/svg" 133 + fill="none" 134 + viewBox="0 0 24 24" 135 + stroke-width="1.5" 136 + stroke="currentColor" 137 + class="size-6" 138 + > 139 + <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /> 140 + </svg> 141 + </button> 114 142 {:else} 115 143 <button 116 - class="fixed bottom-2 left-2 z-50 rounded-xl border border-base-800 bg-base-900/75 p-2 backdrop-blur-sm md:hidden" 144 + class="fixed bottom-2 left-2 z-50 rounded-lg border border-base-800 bg-base-900/75 p-2 backdrop-blur-sm md:hidden" 117 145 onclick={() => showSidebar.toggle()} 118 146 > 119 147 <span class="sr-only">Open Menu</span>
+14 -10
src/routes/+page.svelte
··· 1 1 <script> 2 + import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte'; 2 3 import Container from '$lib/Components/Container.svelte'; 3 4 import ReviewList from '$lib/Components/ReviewList.svelte'; 4 5 import { rateMovieModal, showLoginModal } from '$lib/state.svelte'; ··· 10 11 <title>skywatched</title> 11 12 </svelte:head> 12 13 13 - <Container class="max-w-8xl relative z-10 w-full md:max-w-3xl lg:max-w-5xl xl:max-w-6xl"> 14 - <div class="max-w-8xl mx-auto flex w-full flex-col md:flex-row"> 15 - <div class="flex grow flex-col gap-4 py-8 md:border-r border-base-900"> 16 - <h1 class="text-balance px-8 text-3xl pb-6 font-semibold tracking-tight text-base-100"> 17 - Recent reviews 18 - </h1> 14 + <BaseHeadTags /> 15 + 16 + <Container class="relative z-10 w-full"> 17 + <div class="max-w-8xl mx-auto flex w-full flex-col md:flex-col"> 18 + <div class="mx-auto flex max-w-2xl grow flex-col gap-4 py-8"> 19 + <h1 class="pb-4 text-2xl font-bold">Recent reviews</h1> 20 + 19 21 {#if data.feed.length > 0} 20 - <ReviewList reviews={data.feed}/> 22 + <ReviewList reviews={data.feed} /> 21 23 {/if} 22 24 </div> 23 25 24 - <div class="order-first md:order-last px-8 text-base-100 h-96 md:h-screen flex flex-col items-center justify-center md:max-w-84"> 25 - <h1 class="tracking-tight font-semibold text-balance text-3xl sm:text-5xl">skywatched</h1> 26 - <p class="text-md mt-8 text-pretty font-medium text-center text-base-300 sm:text-lg max-w-xs"> 26 + <div 27 + class="md:max-w-84 order-first flex h-96 flex-col items-center justify-center px-8 text-base-100" 28 + > 29 + <h1 class="text-balance text-3xl font-semibold tracking-tight sm:text-5xl">skywatched</h1> 30 + <p class="text-md mt-8 max-w-xs text-pretty text-center font-medium text-base-300 sm:text-lg"> 27 31 review movies and shows with your bluesky friends. 28 32 </p> 29 33 {#if !data.user}
+18 -7
src/routes/[kind]/[id]/+page.svelte
··· 3 3 import { cn, nameToId } from '$lib/utils'; 4 4 import { rateMovieModal, videoPlayer, watchedItems } from '$lib/state.svelte'; 5 5 6 - 7 6 import Container from '$lib/Components/Container.svelte'; 8 7 import ItemsList from '$lib/Components/ItemsList.svelte'; 9 8 import ReviewList from '$lib/Components/ReviewList.svelte'; ··· 16 15 <svelte:head> 17 16 <title>{data.result.title ?? data.result.name ?? ''} | skywatched</title> 18 17 19 - <meta name="description" content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" /> 18 + <meta 19 + name="description" 20 + content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" 21 + /> 20 22 21 - <meta property="og:url" content="{$page.url.href}" /> 23 + <meta property="og:url" content={$page.url.href} /> 22 24 <meta property="og:type" content="website" /> 23 25 <meta property="og:title" content="{data.result.title ?? data.result.name ?? ''} | skywatched" /> 24 - <meta property="og:description" content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" /> 26 + <meta 27 + property="og:description" 28 + content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" 29 + /> 25 30 <meta property="og:image" content="{$page.url.href}/og.png" /> 26 31 27 32 <meta name="twitter:card" content="summary_large_image" /> 28 33 <meta property="twitter:domain" content="skywatched.app" /> 29 - <meta property="twitter:url" content="{$page.url.href}" /> 34 + <meta property="twitter:url" content={$page.url.href} /> 30 35 <meta name="twitter:title" content="{data.result.title ?? data.result.name ?? ''} | skywatched" /> 31 - <meta name="twitter:description" content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" /> 36 + <meta 37 + name="twitter:description" 38 + content="rate and review {data.result.title ?? data.result.name ?? ''} on skywatched" 39 + /> 32 40 <meta name="twitter:image" content="{$page.url.href}/og.png" /> 33 41 </svelte:head> 34 42 ··· 166 174 167 175 <div class={cn('flex gap-x-6 overflow-x-auto')}> 168 176 {#each data.cast as castMember} 169 - <a href={`/cast/${castMember.id}-${nameToId(castMember.name)}`} class="flex flex-col items-center gap-1"> 177 + <a 178 + href={`/cast/${castMember.id}-${nameToId(castMember.name)}`} 179 + class="flex flex-col items-center gap-1" 180 + > 170 181 <Avatar 171 182 src={castMember.profile_path 172 183 ? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
+4 -4
src/routes/[kind]/[id]/og.png/+server.ts
··· 28 28 <h2 tw="flex flex-col text-7xl font-bold text-zinc-100 text-left px-12 max-w-3xl"> 29 29 <span tw="flex tracking-tight">${data.title ?? data.name}</span> 30 30 </h2> 31 - 32 - 33 31 </div> 34 32 35 - <div tw="flex text-5xl py-8 text-sky-400"> 36 - rate on skywatched.app 33 + <div tw="flex text-4xl text-sky-400 items-end justify-end w-full px-8"> 34 + <div tw="flex"> 35 + rate on skywatched.app 36 + </div> 37 37 </div> 38 38 </div>`; 39 39 };
+3
src/routes/cast/[id]/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import Avatar from '$lib/Components/Avatar.svelte'; 3 + import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte'; 3 4 import Container from '$lib/Components/Container.svelte'; 4 5 import ItemsGrid from '$lib/Components/ItemsGrid.svelte'; 5 6 import { onMount } from 'svelte'; ··· 22 23 23 24 let showFullBiography = $state(false); 24 25 </script> 26 + 27 + <BaseHeadTags /> 25 28 26 29 <Container class="relative z-10 pb-8 pt-4"> 27 30 <div class="flex items-center gap-4 px-4 pt-8">
+5 -2
src/routes/feed/+page.svelte
··· 3 3 import { onMount } from 'svelte'; 4 4 import { type PageData } from './$types'; 5 5 import ReviewList from '$lib/Components/ReviewList.svelte'; 6 + import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte'; 6 7 7 8 let { data }: { data: PageData } = $props(); 8 9 </script> ··· 11 12 <title>feed | skywatched</title> 12 13 </svelte:head> 13 14 14 - <Container class="relative z-10 py-8 sm:py-16"> 15 - <div class="mx-auto max-w-2xl"> 15 + <BaseHeadTags /> 16 + 17 + <Container class="relative z-10 w-full py-8 sm:py-16"> 18 + <div class="mx-auto w-full max-w-2xl"> 16 19 <div class="flex flex-col gap-4 px-4"> 17 20 <h1 class="pb-4 text-2xl font-bold">Recent reviews</h1> 18 21 </div>
+39 -1
src/routes/review/[uri]/+page.svelte
··· 1 1 <script lang="ts"> 2 - import { onMount } from 'svelte'; 3 2 import ReviewCard from '$lib/Components/ReviewCard.svelte'; 4 3 import Container from '$lib/Components/Container.svelte'; 4 + import { page } from '$app/stores'; 5 5 6 6 let { data } = $props(); 7 7 </script> 8 8 9 + <svelte:head> 10 + <title>{data.record.author.displayName || data.record.author.handle}'s review | skywatched</title> 11 + 12 + <meta 13 + name="description" 14 + content="{data.record.author.displayName || data.record.author.handle} reviewed {data.record 15 + .record.metadata?.title ?? ''} on skywatched" 16 + /> 17 + 18 + <meta property="og:url" content={$page.url.href} /> 19 + <meta property="og:type" content="website" /> 20 + <meta 21 + property="og:title" 22 + content="{data.record.author.displayName || data.record.author.handle}'s review | skywatched" 23 + /> 24 + <meta 25 + property="og:description" 26 + content="{data.record.author.displayName || data.record.author.handle} reviewed {data.record 27 + .record.metadata?.title ?? ''} on skywatched" 28 + /> 29 + <meta property="og:image" content="{$page.url.href}/og.png" /> 30 + 31 + <meta name="twitter:card" content="summary_large_image" /> 32 + <meta property="twitter:domain" content="skywatched.app" /> 33 + <meta property="twitter:url" content={$page.url.href} /> 34 + <meta 35 + name="twitter:title" 36 + content="{data.record.author.displayName || data.record.author.handle}'s review | skywatched" 37 + /> 38 + <meta 39 + name="twitter:description" 40 + content="{data.record.author.displayName || data.record.author.handle} reviewed {data.record 41 + .record.metadata?.title ?? ''} on skywatched" 42 + /> 43 + <meta name="twitter:image" content="{$page.url.href}/og.png" /> 44 + </svelte:head> 45 + 9 46 {#if data.record.record.metadata?.backdrop_path} 10 47 <img 11 48 src="https://image.tmdb.org/t/p/w780{data.record.record.metadata.backdrop_path}" ··· 13 50 class="fixed h-full w-full object-cover object-center opacity-20" 14 51 /> 15 52 {/if} 53 + 16 54 <div class="fixed inset-0 h-full w-full bg-black/50"></div> 17 55 18 56 <Container class="z-10 flex h-screen flex-col items-center justify-center">
+92
src/routes/review/[uri]/og.png/+server.ts
··· 1 + // src/routes/og/+server.ts 2 + import { getRecordByUri, type MainRecord } from '$lib/db'; 3 + import { ImageResponse } from '@ethercorps/sveltekit-og'; 4 + import { error, type RequestHandler } from '@sveltejs/kit'; 5 + 6 + const template = (data: MainRecord) => { 7 + return ` 8 + <div tw="bg-zinc-900 flex flex-col w-full h-full items-center justify-center"> 9 + <div tw="flex absolute bottom-0 left-0 right-0 top-0 bg-sky-400"> 10 + <img src="https://image.tmdb.org/t/p/w780${data.record.metadata?.backdrop_path}" alt="" class="flex h-full w-full rounded-xl opacity-50" /> 11 + 12 + <div tw="flex absolute h-full w-full bg-black/80"> 13 + </div> 14 + </div> 15 + <div tw="flex flex-row w-full py-12 px-4 items-center justify-start p-8"> 16 + <div tw="flex h-auto aspect-[3/2] w-82"> 17 + <img src="https://image.tmdb.org/t/p/w500${data.record.metadata?.poster_path}" alt="" 18 + class="flex h-full w-full rounded-xl border-2 border-zinc-800" style="border-radius: 16px; border-width: 1px; border-color: #3f3f46;" /> 19 + </div> 20 + 21 + <div tw="flex flex-col text-7xl font-bold text-zinc-100 text-left px-8 max-w-3xl"> 22 + <div tw="flex items-center"> 23 + ${data.author.avatar ? `<img src=${data.author.avatar} tw="w-16 h-16 rounded-full" />` : ``} 24 + <div tw="flex truncate text-4xl text-zinc-100 pl-4"> 25 + ${data.author.displayName || data.author.handle} 26 + <span tw="flex truncate text-sky-500 pl-2"> 27 + reviewed 28 + </span> 29 + </div> 30 + </div> 31 + 32 + <span tw="flex tracking-tight mt-4">${data.record.metadata?.title}</span> 33 + 34 + <div tw="flex items-center mt-8"> 35 + <div tw="flex items-center"> 36 + 37 + ${new Array(5) 38 + .fill(0) 39 + .map( 40 + (_, i) => `<svg 41 + tw="w-20 h-20 flex ${i * 2 < (data.record.rating?.value ?? 0) ? 'text-sky-400' : 'text-zinc-600'}" 42 + viewBox="0 0 24 24" 43 + fill="currentColor"> 44 + <path 45 + fill-rule="evenodd" 46 + d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.006 5.404.434c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.434 2.082-5.005Z" 47 + clip-rule="evenodd" 48 + /> 49 + </svg>` 50 + ) 51 + .join('')} 52 + </div> 53 + </div> 54 + </div> 55 + </div> 56 + 57 + <div tw="flex text-4xl text-sky-400 items-end justify-end w-full px-6 pb-4"> 58 + <div tw="flex"> 59 + skywatched.app 60 + </div> 61 + </div> 62 + </div>`; 63 + }; 64 + 65 + const host = import.meta.env.DEV ? 'http://localhost:5173' : 'https://skywatched.app'; 66 + const fontPath = `fonts/inter-latin-ext-400-normal.woff`; 67 + const fontFile = await fetch(`${host}/${fontPath}`); 68 + const fontData: ArrayBuffer = await fontFile.arrayBuffer(); 69 + 70 + export const GET: RequestHandler = async ({ params }) => { 71 + const uri = params.uri; 72 + 73 + if (!uri) { 74 + return error(404, 'Not found'); 75 + } 76 + 77 + const record = await getRecordByUri({ uri }); 78 + 79 + return new ImageResponse( 80 + template(record), 81 + { 82 + fonts: [ 83 + { 84 + name: 'Inter Latin', 85 + data: fontData, 86 + weight: 400 87 + } 88 + ] 89 + }, 90 + {} 91 + ); 92 + };
-56
src/routes/review/[uri]/og/+server.ts
··· 1 - // src/routes/og/+server.ts 2 - import { getRecordByUri, type MainRecord } from '$lib/db'; 3 - import { ImageResponse } from '@ethercorps/sveltekit-og'; 4 - import { error, type RequestHandler } from '@sveltejs/kit'; 5 - 6 - const template = (data: MainRecord) => { 7 - return ` 8 - <div tw="bg-zinc-900 flex w-full h-full items-center justify-center"> 9 - 10 - <div tw="flex absolute bottom-0 left-0 right-0 top-0 bg-sky-400"> 11 - <img src="https://image.tmdb.org/t/p/w780${data.record.metadata?.backdrop_path}" alt="" class="flex h-full w-full rounded-xl opacity-50" /> 12 - 13 - <div tw="flex absolute h-full w-full bg-black/80"> 14 - </div> 15 - </div> 16 - <div tw="flex flex-row w-full py-12 px-4 items-center justify-start p-8"> 17 - <div tw="flex h-auto aspect-[3/2] w-82"> 18 - <img src="https://image.tmdb.org/t/p/w500${data.record.metadata?.poster_path}" alt="" 19 - class="flex h-full w-full rounded-xl border-2 border-zinc-800" style="border-radius: 16px; border-width: 1px; border-color: #3f3f46;" /> 20 - </div> 21 - 22 - <h2 tw="flex flex-col text-7xl font-bold text-zinc-100 text-left px-8 max-w-3xl"> 23 - <span tw="flex tracking-tight">${data.record.metadata?.title}</span> 24 - </h2> 25 - </div> 26 - </div>`; 27 - }; 28 - 29 - const host = import.meta.env.DEV ? 'http://localhost:5173' : 'https://skywatched.app'; 30 - const fontPath = `fonts/inter-latin-ext-400-normal.woff`; 31 - const fontFile = await fetch(`${host}/${fontPath}`); 32 - const fontData: ArrayBuffer = await fontFile.arrayBuffer(); 33 - 34 - export const GET: RequestHandler = async ({ params }) => { 35 - const uri = params.uri; 36 - 37 - if (!uri) { 38 - return error(404, 'Not found'); 39 - } 40 - 41 - const record = await getRecordByUri({ uri }); 42 - 43 - return new ImageResponse( 44 - template(record), 45 - { 46 - fonts: [ 47 - { 48 - name: 'Inter Latin', 49 - data: fontData, 50 - weight: 400 51 - } 52 - ] 53 - }, 54 - {} 55 - ); 56 - };
+31 -6
src/routes/search/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import ItemsGrid from '$lib/Components/ItemsGrid.svelte'; 3 3 import Container from '$lib/Components/Container.svelte'; 4 + import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte'; 4 5 5 6 import { type PageData } from './$types'; 6 7 let { data }: { data: PageData } = $props(); ··· 9 10 <svelte:head> 10 11 <title>search | skywatched</title> 11 12 </svelte:head> 13 + 14 + <BaseHeadTags /> 12 15 13 16 <Container> 14 17 <div class="px-4 py-16"> ··· 20 23 type="text" 21 24 name="query" 22 25 id="query" 23 - class="block w-full rounded-md border-0 bg-white/5 py-1.5 pr-14 text-base-50 shadow-sm ring-1 ring-inset ring-base-300/20 placeholder:text-base-400 focus:ring-2 focus:ring-inset focus:ring-accent-600 sm:text-sm/6" 26 + class="block w-full rounded-md border-0 bg-base-900 py-1.5 pr-14 text-base-50 shadow-sm ring-1 ring-inset ring-base-700 placeholder:text-base-400 focus:ring-2 focus:ring-inset focus:ring-accent-600 sm:text-sm/6" 24 27 /> 25 - <div class="absolute inset-y-0 right-0 flex py-1.5 pr-1.5"> 26 - <kbd 27 - class="inline-flex items-center rounded border border-base-200/20 px-1 font-sans text-xs text-base-400" 28 - >Enter</kbd 28 + <button 29 + onsubmit={(event) => { 30 + if (data.query.length === 0) { 31 + event.preventDefault(); 32 + return; 33 + } 34 + }} 35 + type="submit" 36 + class="absolute inset-y-0 right-0 flex py-2 pr-2 text-accent-500 hover:text-accent-400" 37 + > 38 + <svg 39 + xmlns="http://www.w3.org/2000/svg" 40 + fill="none" 41 + viewBox="0 0 24 24" 42 + stroke-width="1.5" 43 + stroke="currentColor" 44 + class="size-5" 45 + aria-hidden="true" 29 46 > 30 - </div> 47 + <path 48 + stroke-linecap="round" 49 + stroke-linejoin="round" 50 + d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" 51 + /> 52 + </svg> 53 + 54 + <span class="sr-only">search</span> 55 + </button> 31 56 </form> 32 57 33 58 {#if data.results.length > 0}
+3
src/routes/user/[handle]/+page.svelte
··· 1 1 <script lang="ts"> 2 + import BaseHeadTags from '$lib/Components/BaseHeadTags.svelte'; 2 3 import Container from '$lib/Components/Container.svelte'; 3 4 import Profile from '$lib/Components/Profile.svelte'; 4 5 import ReviewList from '$lib/Components/ReviewList.svelte'; ··· 9 10 <svelte:head> 10 11 <title>{data.profile.displayName || data.profile.handle}'s reviews</title> 11 12 </svelte:head> 13 + 14 + <BaseHeadTags /> 12 15 13 16 <Container> 14 17 <Profile profile={data.profile} />