refactor(bento): rewrite with css grid

This commit is contained in:
Jason
2024-06-20 12:26:42 -07:00
parent 43bee0c451
commit 07293d6090
28 changed files with 581 additions and 538 deletions

View File

@@ -31,7 +31,10 @@ export async function POST(req: NextRequest): Promise<NextResponse> {
const hash = await generateHash(ip)
const dedupKey = `deduplicate:${hash}:${slug}`
const isNew = await redis.set(dedupKey, true, { nx: true, ex: 24 * 60 * 60 })
const isNew = await redis.set(dedupKey, true, {
nx: true,
ex: 24 * 60 * 60,
})
if (!isNew) {
return new NextResponse('Duplicate request', { status: 202 })

View File

@@ -6,7 +6,9 @@ const POSTS_PER_PAGE = 5
export const generateStaticParams = async () => {
const totalPages = Math.ceil(allBlogs.length / POSTS_PER_PAGE)
const paths = Array.from({ length: totalPages }, (_, i) => ({ page: (i + 1).toString() }))
const paths = Array.from({ length: totalPages }, (_, i) => ({
page: (i + 1).toString(),
}))
return paths
}

View File

@@ -217,3 +217,11 @@ h2,
h3 {
@apply scroll-mt-24;
}
#intro {
@apply bg-blue-300;
}
#bento-box:has(> #detail:hover) > #intro {
@apply bg-red-300;
}

View File

@@ -90,11 +90,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#E9D3B6" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#E9D3B6" />
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
<body className="bg-background pl-[calc(100vw-100%)] text-black antialiased dark:text-white">
<body className="bg-background text-black antialiased dark:text-white">
<ThemeProviders>
<Analytics />
<SectionContainer>
<div className="flex h-full flex-col justify-between font-sans">
<div className="box-border flex h-full flex-col justify-between font-sans">
<SearchProvider searchConfig={siteMetadata.search as SearchConfig}>
<Header />
<main className="mb-auto">{children}</main>

View File

@@ -3,9 +3,7 @@ import BentoBox from '@/components/bento/BentoBox'
export default function Home({ posts }) {
return (
<div className="divide-y divide-accent-foreground dark:divide-accent">
<div className="mx-auto bento-md:-mx-[5vw] bento-lg:-mx-[20vw]">
<BentoBox posts={posts} />
</div>
<BentoBox posts={posts} />
</div>
)
}

View File

@@ -48,7 +48,7 @@ const Card = ({ title, description, imgSrc, href, tags = [] }: CardProps) => (
{tags.map((tag, index) => (
<Badge
key={tag}
className="mr-2 mb-2"
className="mb-2 mr-2"
variant={index === 0 ? 'default' : 'outline'}
>
{tag}

View File

@@ -10,7 +10,7 @@ export default function CountryFlag({ country }: CountryFlagProps) {
<img
src={`https://flagcdn.com/20x15/${country}.png`}
alt={country}
className="inline-block align-text-middle !m-0 !mr-1 rounded-none border-none"
className="align-text-middle !m-0 !mr-1 inline-block rounded-none border-none"
/>
)
}

View File

@@ -43,7 +43,7 @@ export default function Footer() {
Homepage assets by{' '}
<Link
href="https://freepik.com"
className="underline text-muted-foreground/75"
className="text-muted-foreground/75 underline"
>
Freepik
</Link>

View File

@@ -31,41 +31,49 @@ const Header = () => {
}, [])
return (
<header
className={cn(
'fixed inset-x-0 top-4 left-[calc(100vw-100%)] z-40 flex h-[60px] mx-8 bento-md:mx-auto items-center justify-between rounded-3xl bg-secondary border-border border px-4 bento-md:px-8 shadow-sm saturate-100 backdrop-blur-[10px] transition-all duration-200 bento-md:max-w-[768px] bento-lg:max-w-[1168px]',
isScrolled && 'bg-background/80 border-transparent'
)}
>
<div className="w-full mx-auto flex h-[60px] items-center justify-between">
<div>
<Link href="/" aria-label={siteMetadata.headerTitle}>
<div className="flex items-center justify-between">
<NextImage src={Logo} alt="Logo" width="40" height="40" title="Logo" />
</div>
</Link>
</div>
<div className="flex items-center md:space-x-3">
<ul className="hidden space-x-2 md:flex">
{headerNavLinks.map((link, i) => (
<li key={i}>
<Button
variant="ghost"
className="px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground"
>
<Link
// className="rounded px-3 py-2 text-sm font-medium text-muted-foreground transition-all duration-300 hover:bg-secondary hover:brightness-125"
href={link.href}
<header className="fixed inset-x-0 top-4 z-40 flex h-[60px] justify-center">
<div
className={cn(
'mx-6 w-full max-w-[375px] items-center justify-between rounded-3xl border border-border bg-secondary px-4 shadow-sm saturate-100 backdrop-blur-[10px] sm:max-w-screen-sm xl:max-w-screen-xl',
isScrolled && 'border-transparent bg-background/80'
)}
>
<div className="mx-auto flex h-[60px] w-full items-center justify-between">
<div>
<Link href="/" aria-label={siteMetadata.headerTitle}>
<div className="flex items-center justify-between">
<NextImage
src={Logo}
alt="Logo"
width="40"
height="40"
title="Logo"
/>
</div>
</Link>
</div>
<div className="flex items-center md:space-x-3">
<ul className="hidden space-x-2 md:flex">
{headerNavLinks.map((link, i) => (
<li key={i}>
<Button
variant="ghost"
className="px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground"
>
{link.title}
</Link>
</Button>
</li>
))}
</ul>
<SearchButton />
{/* <ThemeSwitch /> */}
<MobileNav />
<Link
// className="rounded px-3 py-2 text-sm font-medium text-muted-foreground transition-all duration-300 hover:bg-secondary hover:brightness-125"
href={link.href}
>
{link.title}
</Link>
</Button>
</li>
))}
</ul>
<SearchButton />
{/* <ThemeSwitch /> */}
<MobileNav />
</div>
</div>
</div>
</header>

View File

@@ -1,28 +0,0 @@
import { Inter } from 'next/font/google'
import { ReactNode } from 'react'
import Footer from './Footer'
import Header from './Header'
import SectionContainer from './SectionContainer'
interface Props {
children: ReactNode
}
const inter = Inter({
subsets: ['latin'],
})
const LayoutWrapper = ({ children }: Props) => {
return (
<SectionContainer>
<div className={`${inter.className} flex h-full flex-col justify-between font-sans`}>
<Header />
<main className="mb-auto">{children}</main>
<Footer />
</div>
</SectionContainer>
)
}
export default LayoutWrapper

View File

@@ -116,7 +116,7 @@ const TOCInline = ({
<a
href={item.url}
className={cn(
'inline-block mb-1',
'mb-1 inline-block',
(item.url.substring(1, item.url.length) === activeId ||
item.active) &&
'active-header'

View File

@@ -14,7 +14,9 @@ import GithubCalendar from './GithubCalendar'
import SilhouetteHover from './SilhouetteHover'
import SpotifyPresence from './SpotifyPresence'
const ResponsiveGridLayout = WidthProvider(Responsive, { measureBeforeMount: true })
const ResponsiveGridLayout = WidthProvider(Responsive, {
measureBeforeMount: true,
})
const BentoBox = ({ posts }) => {
const lanyard = useLanyard({
@@ -72,247 +74,315 @@ const BentoBox = ({ posts }) => {
}, [])
return (
<ResponsiveGridLayout
className="mx-auto max-w-[375px] bento-md:max-w-[800px] bento-lg:max-w-[1200px]"
layouts={{ lg: lgLayout, md: mdLayout, sm: smLayout }}
// I don't know why but if I don't subtract 1 everything shits itself
breakpoints={{ lg: 1199, md: 799, sm: 374 }}
cols={{ lg: 4, md: 4, sm: 2 }}
rowHeight={rowHeight}
isDraggable={false}
isResizable={false}
onWidthChange={handleWidthChange}
isBounded
margin={[16, 16]}
// useCSSTransforms={false}
onDragStart={(layout, oldItem, newItem, placeholder, e, element) =>
handleDragStart(element)
}
onDragStop={(layout, oldItem, newItem, placeholder, e, element) =>
handleDragStop(element)
}
>
<div key="intro" className="aspect-square">
<Image
src="/static/images/bento/bento-intro-silhouette.svg"
alt="Bento Intro Silhouette"
fill
className={`hidden bento-md:block rounded-3xl object-cover transition-opacity duration-300 ${
introSilhouette ? 'opacity-100' : 'opacity-0 delay-75'
}`}
skeletonClassName="rounded-3xl"
noRelative
unoptimized
priority
/>
<Image
src="/static/images/bento/bento-intro.svg"
alt="Bento Intro"
fill
className={`hidden bento-md:block rounded-3xl object-cover transition-opacity duration-300 ${
introSilhouette ? 'opacity-0 delay-75' : 'opacity-100'
}`}
skeletonClassName="rounded-3xl"
noRelative
unoptimized
priority
/>
<Image
src="/static/images/bento/bento-intro-square-silhouette.svg"
alt="Bento Intro Silhouette"
fill
className={`block bento-md:hidden rounded-3xl object-cover transition-opacity duration-300 ${
introSilhouette ? 'opacity-100' : 'opacity-0 delay-75'
}`}
skeletonClassName="rounded-3xl"
noRelative
unoptimized
priority
/>
<Image
src="/static/images/bento/bento-intro-square.svg"
alt="Bento Intro"
fill
className={`block bento-md:hidden rounded-3xl object-cover transition-opacity duration-300 ${
introSilhouette ? 'opacity-0 delay-75' : 'opacity-100'
}`}
skeletonClassName="rounded-3xl"
noRelative
unoptimized
priority
/>
</div>
<div
key="github"
className="group"
onMouseEnter={() => setIntroSilhouette(true)}
onMouseLeave={() => setIntroSilhouette(false)}
>
<div className="relative flex h-full w-full items-center justify-center rounded-lg">
<FaGithub className="absolute z-[1] text-primary w-1/2 h-1/2 bento-md:w-24 bento-md:h-24" />
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-github-silhouette.svg"
silhouetteAlt="Bento Github Silhouette"
mainSrc="/static/images/bento/bento-github.svg"
mainAlt="Bento Github"
className="rounded-3xl object-cover"
/>
<ExternalLink href="https://github.com/jktrn" />
// <ResponsiveGridLayout
// className="mx-auto max-w-[375px] bento-md:max-w-[800px] bento-lg:max-w-[1200px]"
// layouts={{ lg: lgLayout, md: mdLayout, sm: smLayout }}
// // I don't know why but if I don't subtract 1 everything shits itself
// breakpoints={{ lg: 1199, md: 799, sm: 374 }}
// cols={{ lg: 4, md: 4, sm: 2 }}
// rowHeight={rowHeight}
// isDraggable={false}
// isResizable={false}
// onWidthChange={handleWidthChange}
// isBounded
// margin={[16, 16]}
// // useCSSTransforms={false}
// onDragStart={(layout, oldItem, newItem, placeholder, e, element) =>
// handleDragStart(element)
// }
// onDragStop={(layout, oldItem, newItem, placeholder, e, element) =>
// handleDragStop(element)
// }
// >
// <div key="intro" className="aspect-square">
// <Image
// src="/static/images/bento/bento-intro-silhouette.svg"
// alt="Bento Intro Silhouette"
// fill
// className={`hidden bento-md:block rounded-3xl object-cover transition-opacity duration-300 ${
// introSilhouette ? 'opacity-100' : 'opacity-0 delay-75'
// }`}
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// priority
// />
// <Image
// src="/static/images/bento/bento-intro.svg"
// alt="Bento Intro"
// fill
// className={`hidden bento-md:block rounded-3xl object-cover transition-opacity duration-300 ${
// introSilhouette ? 'opacity-0 delay-75' : 'opacity-100'
// }`}
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// priority
// />
// <Image
// src="/static/images/bento/bento-intro-square-silhouette.svg"
// alt="Bento Intro Silhouette"
// fill
// className={`block bento-md:hidden rounded-3xl object-cover transition-opacity duration-300 ${
// introSilhouette ? 'opacity-100' : 'opacity-0 delay-75'
// }`}
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// priority
// />
// <Image
// src="/static/images/bento/bento-intro-square.svg"
// alt="Bento Intro"
// fill
// className={`block bento-md:hidden rounded-3xl object-cover transition-opacity duration-300 ${
// introSilhouette ? 'opacity-0 delay-75' : 'opacity-100'
// }`}
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// priority
// />
// </div>
// <div
// key="github"
// className="group"
// onMouseEnter={() => setIntroSilhouette(true)}
// onMouseLeave={() => setIntroSilhouette(false)}
// >
// <div className="relative flex h-full w-full items-center justify-center rounded-lg">
// <FaGithub className="absolute z-[1] text-primary w-1/2 h-1/2 bento-md:w-24 bento-md:h-24" />
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-github-silhouette.svg"
// silhouetteAlt="Bento Github Silhouette"
// mainSrc="/static/images/bento/bento-github.svg"
// mainAlt="Bento Github"
// className="rounded-3xl object-cover"
// />
// <ExternalLink href="https://github.com/jktrn" />
// </div>
// </div>
// <div key="image-1">
// <Image
// src="/static/images/bento/bento-image-1.svg"
// alt="Bento Box 1"
// fill
// noRelative
// className="rounded-3xl object-cover"
// skeletonClassName="rounded-3xl"
// unoptimized
// priority
// />
// </div>
// <div key="discord">
// {lanyard.data && !lanyard.isValidating ? (
// <DiscordPresence lanyard={lanyard.data} onLoad={() => setDiscordLoaded(true)} />
// ) : (
// <Skeleton className="w-full h-full rounded-3xl" />
// )}
// </div>
// <div
// key="latest-post"
// className="group"
// onMouseEnter={() => setIntroSilhouette(true)}
// onMouseLeave={() => setIntroSilhouette(false)}
// >
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-latest-post-silhouette.svg"
// silhouetteAlt="Bento Latest Post Silhouette"
// mainSrc="/static/images/bento/bento-latest-post.svg"
// mainAlt="Bento Latest Post"
// className="rounded-3xl object-cover flex items-center bento-md:pl-1 bento-lg:pl-3"
// >
// <Image
// src={posts[0].images[0]}
// alt={posts[0].title}
// width={0}
// height={0}
// className="m-2 w-[80%] rounded-2xl border border-border"
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// />
// </SilhouetteHover>
// <ExternalLink href={posts[0].path} newTab={false} />
// </div>
// <div key="image-2">
// <Image
// src="/static/images/bento/bento-image-2.svg"
// alt="Bento Box 2"
// fill
// className="rounded-3xl object-cover"
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// priority
// />
// </div>
// <div
// key="about-ctfs"
// className="group bg-[url('/static/images/bento/bento-about-ctfs-bg.svg')] bg-cover bg-center"
// onMouseEnter={() => setIntroSilhouette(true)}
// onMouseLeave={() => setIntroSilhouette(false)}
// >
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-about-ctfs-silhouette.svg"
// silhouetteAlt="Bento About CTFs Silhouette"
// mainSrc="/static/images/bento/bento-about-ctfs.svg"
// mainAlt="Bento About CTFs"
// className="rounded-3xl object-cover"
// />
// </div>
// <div
// key="twitter"
// className="group"
// onMouseEnter={() => setIntroSilhouette(true)}
// onMouseLeave={() => setIntroSilhouette(false)}
// >
// <div className="relative flex h-full w-full items-center justify-center rounded-lg">
// <FaTwitter className="absolute z-[1] text-primary w-1/2 h-1/2 bento-md:w-24 bento-md:h-24" />
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-twitter-silhouette.svg"
// silhouetteAlt="Bento Twitter Silhouette"
// mainSrc="/static/images/bento/bento-twitter.svg"
// mainAlt="Bento Twitter"
// className="rounded-3xl object-cover"
// />
// <ExternalLink href="https://twitter.com/enscry" />
// </div>
// </div>
// <div
// key="spotify"
// className="group"
// onMouseEnter={() => setIntroSilhouette(true)}
// onMouseLeave={() => setIntroSilhouette(false)}
// >
// {lanyard.data && !lanyard.isValidating ? (
// <SpotifyPresence
// lanyard={lanyard.data}
// onLoad={() => setIsSpotifyLoaded(true)}
// />
// ) : (
// <Skeleton className="w-full h-full rounded-3xl z-[1]" />
// )}
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-spotify-silhouette.svg"
// silhouetteAlt="Bento Spotify Silhouette"
// mainSrc="/static/images/bento/bento-spotify.svg"
// mainAlt="Bento Spotify"
// className="hidden bento-lg:block object-cover rounded-3xl ml-auto"
// />
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-spotify-silhouette-2x1.svg"
// silhouetteAlt="Bento Spotify Silhouette"
// mainSrc="/static/images/bento/bento-spotify-2x1.svg"
// mainAlt="Bento Spotify"
// className="block bento-lg:hidden object-cover rounded-3xl ml-auto"
// />
// </div>
// <div key="tech">
// <Image
// src="/static/images/bento/bento-technologies.svg"
// alt="Bento Technologies"
// fill
// className="rounded-3xl object-cover"
// skeletonClassName="rounded-3xl"
// noRelative
// unoptimized
// />
// </div>
// <div
// key="contributions"
// className="group flex items-center justify-center"
// onMouseEnter={() => setIntroSilhouette(true)}
// onMouseLeave={() => setIntroSilhouette(false)}
// >
// <SilhouetteHover
// silhouetteSrc="/static/images/bento/bento-contributions-silhouette.svg"
// silhouetteAlt="Bento GitHub Contributions Silhouette"
// mainSrc="/static/images/bento/bento-contributions.svg"
// mainAlt="Bento GitHub Contributions"
// className="rounded-3xl object-cover z-[2] flex items-center justify-center p-4"
// >
// <GithubCalendar
// username="jktrn"
// hideColorLegend
// hideTotalCount
// blockMargin={6}
// blockSize={20}
// blockRadius={7}
// />
// </SilhouetteHover>
// </div>
// </ResponsiveGridLayout>
<div className="ml-[calc(-50vw+50%+10px)] w-[calc(100vw-20px)] p-4">
<section className="bento mx-auto grid max-w-[375px] grid-cols-2 gap-4 [grid-template-areas:'a_a''a_a''b_b''b_b''e_e''h_i''h_c''k_c''d_d''d_d''g_g''g_g''f_f''j_j''j_j'] *:rounded-3xl *:border *:border-muted *:bg-secondary sm:max-w-screen-sm sm:[grid-template-areas:'a_a''b_d''e_e''j_g''h_i''h_c''k_c''f_f'] xl:max-w-screen-xl xl:grid-cols-4 xl:[grid-template-areas:'a_a_b_c''d_e_e_c''h_f_f_g''h_i_j_k'] [&:hover>.first:not(:hover)_.silhouette]:opacity-100">
<div className="first aspect-square rounded-3xl border bg-[url('/static/images/bento/bento-intro-square.svg')] bg-cover bg-center bg-no-repeat [grid-area:a] sm:aspect-[2.1/1] sm:bg-[url('/static/images/bento/bento-intro.svg')] xl:aspect-auto">
<div className="silhouette h-full w-full rounded-3xl bg-[url('/static/images/bento/bento-intro-square-silhouette.svg')] bg-cover bg-center bg-no-repeat opacity-0 transition-opacity duration-200 sm:bg-[url('/static/images/bento/bento-intro-silhouette.svg')]" />
<p className="sr-only">
Hey, I'm Jason! I'm a freelance frontend web developer and cybersecurity CTF
player from Los Angeles, and I go by aliases enscribe and jktrn online. I'm
currently interested in open-source intelligence, fundamental UI/UX design,
data science and rhythm games!
</p>
</div>
</div>
<div key="image-1">
<Image
src="/static/images/bento/bento-image-1.svg"
alt="Bento Box 1"
fill
noRelative
className="rounded-3xl object-cover"
skeletonClassName="rounded-3xl"
unoptimized
priority
<div className="aspect-square bg-[url('/static/images/bento/bento-about-ctfs-bg.svg')] bg-cover bg-center bg-no-repeat [grid-area:b] [.bento:hover>&:not(.first):hover_.silhouette]:opacity-100">
<div className="silhouette h-full w-full rounded-3xl bg-[url('/static/images/bento/bento-about-ctfs.svg')] bg-cover bg-center bg-no-repeat opacity-0 transition-opacity duration-200" />
<p className="sr-only">
I currently play cybersecurity capture-the-flags with Project Sekai. Hosted
on this blog are in-depth, beginner-friendly writeups for CTF challenges!
</p>
</div>
<div
className="aspect-[1/2.1] bg-[url('/static/images/bento/bento-image-1.svg')] bg-cover bg-center bg-no-repeat [grid-area:c] xl:aspect-auto"
aria-hidden="true"
/>
</div>
<div key="discord">
{lanyard.data && !lanyard.isValidating ? (
<DiscordPresence lanyard={lanyard.data} onLoad={() => setDiscordLoaded(true)} />
) : (
<Skeleton className="w-full h-full rounded-3xl" />
)}
</div>
<div
key="latest-post"
className="group"
onMouseEnter={() => setIntroSilhouette(true)}
onMouseLeave={() => setIntroSilhouette(false)}
>
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-latest-post-silhouette.svg"
silhouetteAlt="Bento Latest Post Silhouette"
mainSrc="/static/images/bento/bento-latest-post.svg"
mainAlt="Bento Latest Post"
className="rounded-3xl object-cover flex items-center bento-md:pl-1 bento-lg:pl-3"
>
<div className="aspect-square [grid-area:d]">
{lanyard.data && !lanyard.isValidating ? (
<DiscordPresence
lanyard={lanyard.data}
onLoad={() => setDiscordLoaded(true)}
/>
) : (
<Skeleton className="h-full w-full rounded-3xl" />
)}
</div>
<div className="relative flex aspect-[2.1/1] items-center bg-[url('/static/images/bento/bento-latest-post-silhouette.svg')] bg-cover bg-center bg-no-repeat p-4 [grid-area:e] xl:aspect-auto [.bento:hover>&:not(.first):hover_.silhouette]:opacity-100">
<div className="silhouette absolute inset-0 h-full w-full rounded-3xl bg-[url('/static/images/bento/bento-latest-post.svg')] bg-cover bg-center opacity-0 transition-opacity duration-200" />
<Image
src={posts[0].images[0]}
alt={posts[0].title}
width={0}
height={0}
className="m-2 w-[80%] rounded-2xl border border-border"
className="w-[80%] rounded-2xl border border-border sm:ml-2"
skeletonClassName="rounded-3xl"
noRelative
unoptimized
/>
</SilhouetteHover>
<ExternalLink href={posts[0].path} newTab={false} />
</div>
<div key="image-2">
<Image
src="/static/images/bento/bento-image-2.svg"
alt="Bento Box 2"
fill
className="rounded-3xl object-cover"
skeletonClassName="rounded-3xl"
noRelative
unoptimized
priority
/>
</div>
<div
key="about-ctfs"
className="group bg-[url('/static/images/bento/bento-about-ctfs-bg.svg')] bg-cover bg-center"
onMouseEnter={() => setIntroSilhouette(true)}
onMouseLeave={() => setIntroSilhouette(false)}
>
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-about-ctfs-silhouette.svg"
silhouetteAlt="Bento About CTFs Silhouette"
mainSrc="/static/images/bento/bento-about-ctfs.svg"
mainAlt="Bento About CTFs"
className="rounded-3xl object-cover"
/>
</div>
<div
key="twitter"
className="group"
onMouseEnter={() => setIntroSilhouette(true)}
onMouseLeave={() => setIntroSilhouette(false)}
>
<div className="relative flex h-full w-full items-center justify-center rounded-lg">
<FaTwitter className="absolute z-[1] text-primary w-1/2 h-1/2 bento-md:w-24 bento-md:h-24" />
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-twitter-silhouette.svg"
silhouetteAlt="Bento Twitter Silhouette"
mainSrc="/static/images/bento/bento-twitter.svg"
mainAlt="Bento Twitter"
className="rounded-3xl object-cover"
/>
<ExternalLink href="https://twitter.com/enscry" />
<ExternalLink href={posts[0].path} newTab={false} />
</div>
</div>
<div
key="spotify"
className="group"
onMouseEnter={() => setIntroSilhouette(true)}
onMouseLeave={() => setIntroSilhouette(false)}
>
{lanyard.data && !lanyard.isValidating ? (
<SpotifyPresence
lanyard={lanyard.data}
onLoad={() => setIsSpotifyLoaded(true)}
/>
) : (
<Skeleton className="w-full h-full rounded-3xl z-[1]" />
)}
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-spotify-silhouette.svg"
silhouetteAlt="Bento Spotify Silhouette"
mainSrc="/static/images/bento/bento-spotify.svg"
mainAlt="Bento Spotify"
className="hidden bento-lg:block object-cover rounded-3xl ml-auto"
<div className="aspect-[2.1/1] bg-blue-500 [grid-area:f] xl:aspect-auto [.bento:hover>&:not(.first):hover]:bg-red-500">
Contributions
</div>
<div className="aspect-square bg-blue-500 [grid-area:g] [.bento:hover>&:not(.first):hover]:bg-red-500">
Spotify
</div>
<div
className="aspect-[1/2.1] bg-[url('/static/images/bento/bento-image-2.svg')] bg-cover bg-center bg-no-repeat [grid-area:h] xl:aspect-auto"
aria-hidden="true"
/>
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-spotify-silhouette-2x1.svg"
silhouetteAlt="Bento Spotify Silhouette"
mainSrc="/static/images/bento/bento-spotify-2x1.svg"
mainAlt="Bento Spotify"
className="block bento-lg:hidden object-cover rounded-3xl ml-auto"
<div className="aspect-square bg-blue-500 [grid-area:i] [.bento:hover>&:not(.first):hover]:bg-red-500">
GitHub
</div>
<div
className="aspect-square bg-[url('/static/images/bento/bento-technologies.svg')] bg-cover bg-center bg-no-repeat [grid-area:j]"
aria-hidden="true"
/>
</div>
<div key="tech">
<Image
src="/static/images/bento/bento-technologies.svg"
alt="Bento Technologies"
fill
className="rounded-3xl object-cover"
skeletonClassName="rounded-3xl"
noRelative
unoptimized
/>
</div>
<div
key="contributions"
className="group flex items-center justify-center"
onMouseEnter={() => setIntroSilhouette(true)}
onMouseLeave={() => setIntroSilhouette(false)}
>
<SilhouetteHover
silhouetteSrc="/static/images/bento/bento-contributions-silhouette.svg"
silhouetteAlt="Bento GitHub Contributions Silhouette"
mainSrc="/static/images/bento/bento-contributions.svg"
mainAlt="Bento GitHub Contributions"
className="rounded-3xl object-cover z-[2] flex items-center justify-center p-4"
>
<GithubCalendar
username="jktrn"
hideColorLegend
hideTotalCount
blockMargin={6}
blockSize={20}
blockRadius={7}
/>
</SilhouetteHover>
</div>
</ResponsiveGridLayout>
<div className="aspect-square bg-blue-500 [grid-area:k] [.bento:hover>&:not(.first):hover]:bg-red-500">
Twitter
</div>
</section>
</div>
)
}

View File

@@ -25,11 +25,11 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
return (
<>
<div className="flex bento-md:hidden bento-lg:relative bento-lg:flex w-full h-full flex-col">
<div className="absolute top-5 left-4">
<div className="relative flex h-full w-full flex-col">
<div className="absolute left-4 top-5">
<div className="relative">
<Image
className="rounded-full grayscale w-24 h-24"
className="h-24 w-24 rounded-full grayscale"
src={`https://api.lanyard.rest/${lanyard.data.discord_user.id}.png`}
alt="Discord Avatar"
width={0}
@@ -37,32 +37,32 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
unoptimized
/>
{lanyard.data.discord_status === 'online' && (
<div className="absolute bottom-0 right-0 w-6 h-6 flex items-center justify-center rounded-full bg-primary border-4 border-background" />
<div className="absolute bottom-0 right-0 flex h-6 w-6 items-center justify-center rounded-full border-4 border-background bg-primary" />
)}
{lanyard.data.discord_status === 'idle' && (
<div className="absolute bottom-0 right-0 w-6 h-6 rounded-full bg-primary border-4 border-background">
<div className="bg-background w-[10px] h-[10px] rounded-full" />
<div className="absolute bottom-0 right-0 h-6 w-6 rounded-full border-4 border-background bg-primary">
<div className="h-[10px] w-[10px] rounded-full bg-background" />
</div>
)}
{lanyard.data.discord_status === 'dnd' && (
<div className="absolute bottom-0 right-0 w-6 h-6 rounded-full bg-destructive flex items-center justify-center border-4 border-background">
<div className="bg-background w-[11px] h-[4px] rounded-full" />
<div className="absolute bottom-0 right-0 flex h-6 w-6 items-center justify-center rounded-full border-4 border-background bg-destructive">
<div className="h-[4px] w-[11px] rounded-full bg-background" />
</div>
)}
{lanyard.data.discord_status === 'offline' && (
<div className="absolute bottom-0 right-0 w-6 h-6 rounded-full bg-muted-foreground flex items-center justify-center border-4 border-background">
<div className="bg-background w-2 h-2 rounded-full" />
<div className="absolute bottom-0 right-0 flex h-6 w-6 items-center justify-center rounded-full border-4 border-background bg-muted-foreground">
<div className="h-2 w-2 rounded-full bg-background" />
</div>
)}
</div>
</div>
<div className="absolute right-0 top-0 z-[1] w-14 h-14 flex items-center justify-center m-3 rounded-full bg-primary">
<FaDiscord size={50} className="text-secondary p-1" />
<div className="absolute right-0 top-0 z-[1] m-3 flex h-14 w-14 items-center justify-center rounded-full bg-primary">
<FaDiscord size={50} className="p-1 text-secondary" />
</div>
<div className="bg-tertiary/50 w-full h-[80px] rounded-t-3xl flex-shrink-0" />
<div className="m-3 flex flex-col h-full gap-3 overflow-scroll bento-md:overflow-hidden">
<div className="h-[80px] w-full flex-shrink-0 rounded-t-3xl bg-tertiary/50" />
<div className="m-3 flex h-full flex-col gap-3 overflow-scroll sm:overflow-hidden">
<div className="h-6 flex-shrink-0">
<div className="bg-tertiary/50 rounded-lg w-fit h-full ml-auto flex items-center">
<div className="ml-auto flex h-full w-fit items-center rounded-lg bg-tertiary/50">
<Image
src="/static/images/bento/bento-discord-badges.svg"
alt="Discord Badges"
@@ -72,13 +72,13 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
/>
</div>
</div>
<div className="text-sm h-fit px-2 py-1 rounded-lg bg-tertiary/50 leading-snug">
<div className="h-fit rounded-lg bg-tertiary/50 px-2 py-1 text-sm leading-snug">
<div>{lanyard.data.discord_user.display_name}</div>
<div className="text-[10px] text-muted-foreground">
@{lanyard.data.discord_user.username}
</div>
</div>
<div className="flex h-full p-2 rounded-lg bg-tertiary/50 leading-snug gap-2 items-center">
<div className="flex h-full items-center gap-2 rounded-lg bg-tertiary/50 p-2 leading-snug">
{hasMainActivity ? (
<>
<div className="relative">
@@ -87,7 +87,7 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
alt="Discord Activity Image"
width={0}
height={0}
className="w-16 rounded-lg grayscale"
className="w-20 rounded-lg grayscale"
unoptimized
/>
<Image
@@ -95,18 +95,18 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
alt="Now Playing"
width={0}
height={0}
className="absolute -bottom-1 -right-1 w-6 h-6 rounded-full border-2 border-border grayscale"
className="absolute -bottom-1 -right-1 h-6 w-6 rounded-full border-2 border-border grayscale"
unoptimized
/>
</div>
<div className="flex flex-col">
<div className="text-xs leading-relaxed line-clamp-1">
<div className="line-clamp-1 text-xs leading-relaxed">
{mainActivity.name}
</div>
<div className="text-[10px] text-muted-foreground line-clamp-1">
<div className="line-clamp-1 text-[10px] text-muted-foreground">
{mainActivity.details}
</div>
<div className="text-[10px] text-muted-foreground line-clamp-1">
<div className="line-clamp-1 text-[10px] text-muted-foreground">
{mainActivity.state}
</div>
{elapsedTime && (
@@ -117,7 +117,7 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
</div>
</>
) : (
<div className="flex flex-col items-center justify-center w-full h-full gap-1">
<div className="flex h-full w-full flex-col items-center justify-center gap-1">
<Image
src="/static/images/bento/bento-discord-futon.svg"
alt="No Status Image"
@@ -131,111 +131,6 @@ const DiscordPresence = ({ lanyard, onLoad }) => {
</div>
</div>
</div>
<div className="hidden bento-md:flex bento-md:relative bento-lg:hidden w-full h-full flex-col">
<div className="flex flex-col h-full gap-2 m-2 justify-between">
<div className="flex gap-2 items-center">
<div className="flex-shrink-0">
<div className="relative">
<Image
className="w-[55px] h-[55px] bento-md:w-[70px] bento-md:h-[70px] rounded-full grayscale"
src={`https://api.lanyard.rest/${lanyard.data.discord_user.id}.png`}
alt="Discord Avatar"
width={96}
height={96}
unoptimized
/>
{lanyard.data.discord_status === 'online' && (
<div className="absolute -bottom-0 right-0 w-5 h-5 flex items-center justify-center rounded-full bg-primary border-4 border-background" />
)}
{lanyard.data.discord_status === 'idle' && (
<div className="absolute bottom-0 right-0 w-5 h-5 rounded-full bg-primary border-4 border-background">
<div className="bg-background w-[7px] h-[7px] rounded-full" />
</div>
)}
{lanyard.data.discord_status === 'dnd' && (
<div className="absolute bottom-0 right-0 w-5 h-5 rounded-full bg-destructive flex items-center justify-center border-4 border-background">
<div className="bg-background w-2 h-1 rounded-full" />
</div>
)}
{lanyard.data.discord_status === 'offline' && (
<div className="absolute bottom-0 right-0 w-5 h-5 rounded-full bg-muted-foreground flex items-center justify-center border-4 border-background">
<div className="bg-background w-[6px] h-[6px] rounded-full" />
</div>
)}
</div>
</div>
<div className="flex flex-col">
<div className="text-sm leading-snug ml-1">
<div>{lanyard.data.discord_user.display_name}</div>
<div className="text-[10px] text-muted-foreground">
@{lanyard.data.discord_user.username}
</div>
</div>
<Image
src="/static/images/bento/bento-discord-badges.svg"
alt="Discord Badges"
width={0}
height={0}
className="h-full w-full rounded-md grayscale"
/>
</div>
</div>
<div className="flex h-full py-1 px-2 bento-md:p-2 bg-tertiary/50 leading-snug gap-2 items-center rounded-2xl">
{hasMainActivity ? (
<>
<div className="relative flex-shrink-0">
<Image
src={`https://cdn.discordapp.com/app-assets/${mainActivity.application_id}/${mainActivity.assets.large_image}.png`}
alt="Discord Activity Image"
width={0}
height={0}
className="w-16 rounded-lg grayscale"
unoptimized
/>
<Image
src={`https://cdn.discordapp.com/app-assets/${mainActivity.application_id}/${mainActivity.assets.small_image}.png`}
alt="Now Playing"
width={0}
height={0}
className="absolute -bottom-1 -right-1 w-6 h-6 rounded-full border-2 border-border grayscale"
unoptimized
/>
</div>
<div className="flex flex-col w-full">
<div className="text-xs leading-relaxed line-clamp-1">
{mainActivity.name}
</div>
<div className="text-[10px] text-muted-foreground line-clamp-1">
{mainActivity.details}
</div>
<div className="text-[10px] text-muted-foreground line-clamp-1">
{mainActivity.state}
</div>
{elapsedTime && (
<div className="text-[10px] text-muted-foreground">
{elapsedTime}
</div>
)}
</div>
</>
) : (
<div className="flex flex-col items-center justify-center w-full h-full">
<Image
src="/static/images/bento/bento-discord-futon.svg"
alt="No Status Image"
width={0}
height={0}
className="h-full w-fit rounded-lg"
/>
<div className="text-[10px] text-muted-foreground">No status!</div>
</div>
)}
</div>
<div className="absolute right-0 top-0 w-14 h-14 flex items-center justify-center m-3 rounded-full bg-primary">
<FaDiscord size={50} className="text-secondary p-1" />
</div>
</div>
</div>
</>
)
}
@@ -253,9 +148,9 @@ function getElapsedTime(unixTimestamp: number): string {
const seconds = Math.floor(difference / 1000)
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds
return `${hours.toString().padStart(2, '0')}:${minutes
.toString()
.padStart(2, '0')} elapsed`
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')} elapsed`
}
export default DiscordPresence

View File

@@ -51,9 +51,9 @@ const GithubCalendar: FunctionComponent<Props> = ({ username, ...props }) => {
alt="Error"
width={0}
height={0}
className="w-24 bento-lg:w-48 h-auto"
className="h-auto w-24 bento-lg:w-48"
/>
<p className="text-center w-48 bento-lg:w-64 text-muted-foreground text-sm">
<p className="w-48 text-center text-sm text-muted-foreground bento-lg:w-64">
This component is down. Please email me!
</p>
</div>

View File

@@ -55,7 +55,7 @@ const SpotifyPresence = ({ lanyard, onLoad }) => {
return (
<>
<div className="flex bento-md:hidden z-[1] bento-lg:flex h-full w-full flex-col justify-between p-6">
<div className="z-[1] flex h-full w-full flex-col justify-between p-6">
<Image
src={album_art_url}
alt="Album art"
@@ -83,48 +83,16 @@ const SpotifyPresence = ({ lanyard, onLoad }) => {
{song}
</span>
<span className="line-clamp-1 w-[85%] text-xs text-muted-foreground">
<span className="text-secondary-foreground font-semibold">by</span>{' '}
<span className="font-semibold text-secondary-foreground">by</span>{' '}
{artist}
</span>
<span className="line-clamp-1 w-[85%] text-xs text-muted-foreground">
<span className="text-secondary-foreground font-semibold">on</span>{' '}
<span className="font-semibold text-secondary-foreground">on</span>{' '}
{album}
</span>
</div>
</div>
</div>
<div className="hidden bento-md:flex z-[1] bento-lg:hidden h-full w-full px-4 items-center gap-4">
<Image
src={album_art_url}
alt="Album art"
width={0}
height={0}
className="w-32 rounded-xl border border-border grayscale"
unoptimized
/>
<div className="flex flex-col w-[42%]">
<span className="mb-2 flex gap-2">
<Image
src="/static/images/bento/bento-now-playing.svg"
alt="Now playing"
width={16}
height={16}
/>
{lanyard.data.listening_to_spotify ? (
<span className="text-sm text-primary">Now playing...</span>
) : (
<span className="text-sm text-primary">Last played...</span>
)}
</span>
<span className="text-md mb-2 line-clamp-3 font-bold leading-none">{song}</span>
<span className="line-clamp-2 w-[85%] text-xs text-muted-foreground">
<span className="text-secondary-foreground font-semibold">by</span> {artist}
</span>
<span className="line-clamp-2 w-[85%] text-xs text-muted-foreground">
<span className="text-secondary-foreground font-semibold">on</span> {album}
</span>
</div>
</div>
<div className="absolute right-0 top-0 z-[1] m-3 text-primary">
<FaSpotify size={56} />
</div>

View File

@@ -16,18 +16,42 @@ layout: PostBare
<TOCInline
toc={[
{ value: 'Intro', url: '/blog/japan-retrospective#intro', depth: 1 },
{ value: 'Fundamentals', url: '/blog/japan-retrospective#fundamentals', depth: 1 },
{ value: 'The Itinerary', url: '/blog/japan-retrospective#the-itinerary', depth: 1 },
{ value: 'Daily Log', url: '/blog/japan-retrospective#daily-log', depth: 1 },
{
value: 'Fundamentals',
url: '/blog/japan-retrospective#fundamentals',
depth: 1,
},
{
value: 'The Itinerary',
url: '/blog/japan-retrospective#the-itinerary',
depth: 1,
},
{
value: 'Daily Log',
url: '/blog/japan-retrospective#daily-log',
depth: 1,
},
{
value: 'Day 0: Arrival at KIX',
url: '/blog/japan-retrospective/day-0',
depth: 2,
active: true,
},
{ value: 'Day 1: Osaka Day Trip', url: '/blog/japan-retrospective/day-1', depth: 2 },
{ value: 'Day 2: Nara Day Trip', url: '/blog/japan-retrospective/day-2', depth: 2 },
{ value: 'Day 3: East Kyoto', url: '/blog/japan-retrospective/day-3', depth: 2 },
{
value: 'Day 1: Osaka Day Trip',
url: '/blog/japan-retrospective/day-1',
depth: 2,
},
{
value: 'Day 2: Nara Day Trip',
url: '/blog/japan-retrospective/day-2',
depth: 2,
},
{
value: 'Day 3: East Kyoto',
url: '/blog/japan-retrospective/day-3',
depth: 2,
},
{
value: 'Day 4: West/Central Kyoto',
url: '/blog/japan-retrospective/day-4',

View File

@@ -16,18 +16,42 @@ layout: PostBare
<TOCInline
toc={[
{ value: 'Intro', url: '/blog/japan-retrospective#intro', depth: 1 },
{ value: 'Fundamentals', url: '/blog/japan-retrospective#fundamentals', depth: 1 },
{ value: 'The Itinerary', url: '/blog/japan-retrospective#the-itinerary', depth: 1 },
{ value: 'Daily Log', url: '/blog/japan-retrospective#daily-log', depth: 1 },
{ value: 'Day 0: Arrival at KIX', url: '/blog/japan-retrospective/day-0', depth: 2 },
{
value: 'Fundamentals',
url: '/blog/japan-retrospective#fundamentals',
depth: 1,
},
{
value: 'The Itinerary',
url: '/blog/japan-retrospective#the-itinerary',
depth: 1,
},
{
value: 'Daily Log',
url: '/blog/japan-retrospective#daily-log',
depth: 1,
},
{
value: 'Day 0: Arrival at KIX',
url: '/blog/japan-retrospective/day-0',
depth: 2,
},
{
value: 'Day 1: Osaka Day Trip',
url: '/blog/japan-retrospective/day-1',
depth: 2,
active: true,
},
{ value: 'Day 2: Nara Day Trip', url: '/blog/japan-retrospective/day-2', depth: 2 },
{ value: 'Day 3: East Kyoto', url: '/blog/japan-retrospective/day-3', depth: 2 },
{
value: 'Day 2: Nara Day Trip',
url: '/blog/japan-retrospective/day-2',
depth: 2,
},
{
value: 'Day 3: East Kyoto',
url: '/blog/japan-retrospective/day-3',
depth: 2,
},
{
value: 'Day 4: West/Central Kyoto',
url: '/blog/japan-retrospective/day-4',

View File

@@ -16,18 +16,42 @@ layout: PostBare
<TOCInline
toc={[
{ value: 'Intro', url: '/blog/japan-retrospective#intro', depth: 1 },
{ value: 'Fundamentals', url: '/blog/japan-retrospective#fundamentals', depth: 1 },
{ value: 'The Itinerary', url: '/blog/japan-retrospective#the-itinerary', depth: 1 },
{ value: 'Daily Log', url: '/blog/japan-retrospective#daily-log', depth: 1 },
{ value: 'Day 0: Arrival at KIX', url: '/blog/japan-retrospective/day-0', depth: 2 },
{ value: 'Day 1: Osaka Day Trip', url: '/blog/japan-retrospective/day-1', depth: 2 },
{
value: 'Fundamentals',
url: '/blog/japan-retrospective#fundamentals',
depth: 1,
},
{
value: 'The Itinerary',
url: '/blog/japan-retrospective#the-itinerary',
depth: 1,
},
{
value: 'Daily Log',
url: '/blog/japan-retrospective#daily-log',
depth: 1,
},
{
value: 'Day 0: Arrival at KIX',
url: '/blog/japan-retrospective/day-0',
depth: 2,
},
{
value: 'Day 1: Osaka Day Trip',
url: '/blog/japan-retrospective/day-1',
depth: 2,
},
{
value: 'Day 2: Nara Day Trip',
url: '/blog/japan-retrospective/day-2',
depth: 2,
active: true,
},
{ value: 'Day 3: East Kyoto', url: '/blog/japan-retrospective/day-3', depth: 2 },
{
value: 'Day 3: East Kyoto',
url: '/blog/japan-retrospective/day-3',
depth: 2,
},
{
value: 'Day 4: West/Central Kyoto',
url: '/blog/japan-retrospective/day-4',

View File

@@ -16,12 +16,36 @@ layout: PostBare
<TOCInline
toc={[
{ value: 'Intro', url: '/blog/japan-retrospective#intro', depth: 1 },
{ value: 'Fundamentals', url: '/blog/japan-retrospective#fundamentals', depth: 1 },
{ value: 'The Itinerary', url: '/blog/japan-retrospective#the-itinerary', depth: 1 },
{ value: 'Daily Log', url: '/blog/japan-retrospective#daily-log', depth: 1 },
{ value: 'Day 0: Arrival at KIX', url: '/blog/japan-retrospective/day-0', depth: 2 },
{ value: 'Day 1: Osaka Day Trip', url: '/blog/japan-retrospective/day-1', depth: 2 },
{ value: 'Day 2: Nara Day Trip', url: '/blog/japan-retrospective/day-2', depth: 2 },
{
value: 'Fundamentals',
url: '/blog/japan-retrospective#fundamentals',
depth: 1,
},
{
value: 'The Itinerary',
url: '/blog/japan-retrospective#the-itinerary',
depth: 1,
},
{
value: 'Daily Log',
url: '/blog/japan-retrospective#daily-log',
depth: 1,
},
{
value: 'Day 0: Arrival at KIX',
url: '/blog/japan-retrospective/day-0',
depth: 2,
},
{
value: 'Day 1: Osaka Day Trip',
url: '/blog/japan-retrospective/day-1',
depth: 2,
},
{
value: 'Day 2: Nara Day Trip',
url: '/blog/japan-retrospective/day-2',
depth: 2,
},
{
value: 'Day 3: East Kyoto',
url: '/blog/japan-retrospective/day-3',

View File

@@ -16,12 +16,36 @@ layout: PostBare
<TOCInline
toc={[
{ value: 'Intro', url: '/blog/japan-retrospective#intro', depth: 1 },
{ value: 'Fundamentals', url: '/blog/japan-retrospective#fundamentals', depth: 1 },
{ value: 'The Itinerary', url: '/blog/japan-retrospective#the-itinerary', depth: 1 },
{ value: 'Daily Log', url: '/blog/japan-retrospective#daily-log', depth: 1 },
{ value: 'Day 0: Arrival at KIX', url: '/blog/japan-retrospective/day-0', depth: 2 },
{ value: 'Day 1: Osaka Day Trip', url: '/blog/japan-retrospective/day-1', depth: 2 },
{ value: 'Day 2: Nara Day Trip', url: '/blog/japan-retrospective/day-2', depth: 2 },
{
value: 'Fundamentals',
url: '/blog/japan-retrospective#fundamentals',
depth: 1,
},
{
value: 'The Itinerary',
url: '/blog/japan-retrospective#the-itinerary',
depth: 1,
},
{
value: 'Daily Log',
url: '/blog/japan-retrospective#daily-log',
depth: 1,
},
{
value: 'Day 0: Arrival at KIX',
url: '/blog/japan-retrospective/day-0',
depth: 2,
},
{
value: 'Day 1: Osaka Day Trip',
url: '/blog/japan-retrospective/day-1',
depth: 2,
},
{
value: 'Day 2: Nara Day Trip',
url: '/blog/japan-retrospective/day-2',
depth: 2,
},
{
value: 'Day 3: East Kyoto',
url: '/blog/japan-retrospective/day-3',

View File

@@ -63,8 +63,8 @@ I was recently invited by the academic team "DN" (the name, surprisingly, has no
type="warning"
/>
<span className="text-[#FF9999]">**First blood!**</span> Here are the notes the author provided alongside
the challenge description, abridged for brevity:
<span className="text-[#FF9999]">**First blood!**</span> Here are the notes the author provided
alongside the challenge description, abridged for brevity:
<Box
text="- The connection times out after 60 seconds, and there will be 3 iterations.

View File

@@ -213,8 +213,9 @@ We've managed to recover the flag from the first OSINT challenge, but it's actua
Find and crack the master flag list, and submit the flag you see of ours on the list."
/>
<span className="text-[#FF9999]">**First blood!**</span> We're now tasked with analyzing the contents
of their website. Scouring through the source code, we find two extra pages: `/ctfs.html` and `/prices.html`:
<span className="text-[#FF9999]">**First blood!**</span> We're now tasked with analyzing the
contents of their website. Scouring through the source code, we find two extra pages: `/ctfs.html`
and `/prices.html`:
![CTFs](/static/images/wolvctf-2023/website-ctfs.png)

View File

@@ -123,7 +123,7 @@ export default function ListLayout({
type="text"
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search articles"
className="block w-full rounded-md border border-muted-foreground bg-secondary px-4 py-2 text-muted-foreground focus:border-primary focus:ring-primary dark:border-muted placeholder-muted-foreground"
className="block w-full rounded-md border border-muted-foreground bg-secondary px-4 py-2 text-muted-foreground placeholder-muted-foreground focus:border-primary focus:ring-primary dark:border-muted"
/>
</label>
<Search className="absolute right-3 top-3 h-5 w-5 text-muted-foreground" />
@@ -136,7 +136,7 @@ export default function ListLayout({
const isLoadingViewCount = pageViews[slug] === undefined
return (
<li key={path} className="py-4">
<article className="space-y-2 xl:grid xl:grid-cols-5 xl:gap-4 xl:items-start xl:space-y-0">
<article className="space-y-2 xl:grid xl:grid-cols-5 xl:items-start xl:gap-4 xl:space-y-0">
<div className="xl:col-span-2">
<Link href={`/${path}`}>
<div className="aspect-w-16 aspect-h-9">
@@ -145,7 +145,7 @@ export default function ListLayout({
alt={`${title} thumbnail`}
height="0"
width="0"
className="w-full h-fit mb-4 rounded-md"
className="mb-4 h-fit w-full rounded-md"
unoptimized
/>
</div>
@@ -153,7 +153,7 @@ export default function ListLayout({
</div>
<div className="space-y-3 xl:col-span-3">
<div>
<h3 className="text-2xl font-bold leading-8 tracking-tight mb-2">
<h3 className="mb-2 text-2xl font-bold leading-8 tracking-tight">
<Link href={`/${path}`} className="text-foreground">
{title}
</Link>
@@ -171,7 +171,7 @@ export default function ListLayout({
<dd className="flex gap-1 text-base font-medium leading-6 text-muted-foreground">
{isLoadingViewCount ? (
<span className="flex items-center justify-center gap-2">
<Skeleton className="w-12 h-6" />
<Skeleton className="h-6 w-12" />
<span> views</span>
</span>
) : (

View File

@@ -96,7 +96,7 @@ export default function PostLayout({
<span className="text-muted-foreground">
{pageViews.isLoading ? (
<span className="flex items-center justify-center gap-2">
<Skeleton className="w-12 h-6" />
<Skeleton className="h-6 w-12" />
<span> views</span>
</span>
) : (

46
package-lock.json generated
View File

@@ -55,7 +55,7 @@
"remark-math": "^5.1.1",
"slugify": "^1.6.6",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.3.3",
"tailwindcss": "^3.4.4",
"tailwindcss-animate": "^1.0.7",
"unist-util-visit": "^4.1.0"
},
@@ -73,8 +73,8 @@
"eslint-plugin-prettier": "^5.0.0",
"husky": "^8.0.0",
"lint-staged": "^13.0.0",
"prettier": "^3.0.0",
"prettier-plugin-tailwindcss": "^0.4.1",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"typescript": "^5.1.3"
}
},
@@ -12734,9 +12734,9 @@
}
},
"node_modules/prettier": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz",
"integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
@@ -12761,20 +12761,20 @@
}
},
"node_modules/prettier-plugin-tailwindcss": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.4.1.tgz",
"integrity": "sha512-hwn2EiJmv8M+AW4YDkbjJ6HlZCTzLyz1QlySn9sMuKV/Px0fjwldlB7tol8GzdgqtkdPtzT3iJ4UzdnYXP25Ag==",
"version": "0.6.5",
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.5.tgz",
"integrity": "sha512-axfeOArc/RiGHjOIy9HytehlC0ZLeMaqY09mm8YCkMzznKiDkwFzOpBvtuhuv3xG5qB73+Mj7OCe2j/L1ryfuQ==",
"dev": true,
"engines": {
"node": ">=12.17.0"
"node": ">=14.21.3"
},
"peerDependencies": {
"@ianvs/prettier-plugin-sort-imports": "*",
"@prettier/plugin-pug": "*",
"@shopify/prettier-plugin-liquid": "*",
"@shufo/prettier-plugin-blade": "*",
"@trivago/prettier-plugin-sort-imports": "*",
"prettier": "^2.2 || ^3.0",
"@zackad/prettier-plugin-twig-melody": "*",
"prettier": "^3.0",
"prettier-plugin-astro": "*",
"prettier-plugin-css-order": "*",
"prettier-plugin-import-sort": "*",
@@ -12782,9 +12782,9 @@
"prettier-plugin-marko": "*",
"prettier-plugin-organize-attributes": "*",
"prettier-plugin-organize-imports": "*",
"prettier-plugin-sort-imports": "*",
"prettier-plugin-style-order": "*",
"prettier-plugin-svelte": "*",
"prettier-plugin-twig-melody": "*"
"prettier-plugin-svelte": "*"
},
"peerDependenciesMeta": {
"@ianvs/prettier-plugin-sort-imports": {
@@ -12796,10 +12796,10 @@
"@shopify/prettier-plugin-liquid": {
"optional": true
},
"@shufo/prettier-plugin-blade": {
"@trivago/prettier-plugin-sort-imports": {
"optional": true
},
"@trivago/prettier-plugin-sort-imports": {
"@zackad/prettier-plugin-twig-melody": {
"optional": true
},
"prettier-plugin-astro": {
@@ -12823,14 +12823,14 @@
"prettier-plugin-organize-imports": {
"optional": true
},
"prettier-plugin-sort-imports": {
"optional": true
},
"prettier-plugin-style-order": {
"optional": true
},
"prettier-plugin-svelte": {
"optional": true
},
"prettier-plugin-twig-melody": {
"optional": true
}
}
},
@@ -15690,9 +15690,9 @@
}
},
"node_modules/tailwindcss": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz",
"integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==",
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz",
"integrity": "sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -15702,7 +15702,7 @@
"fast-glob": "^3.3.0",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.19.1",
"jiti": "^1.21.0",
"lilconfig": "^2.1.0",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",

View File

@@ -59,7 +59,7 @@
"remark-math": "^5.1.1",
"slugify": "^1.6.6",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.3.3",
"tailwindcss": "^3.4.4",
"tailwindcss-animate": "^1.0.7",
"unist-util-visit": "^4.1.0"
},
@@ -77,8 +77,8 @@
"eslint-plugin-prettier": "^5.0.0",
"husky": "^8.0.0",
"lint-staged": "^13.0.0",
"prettier": "^3.0.0",
"prettier-plugin-tailwindcss": "^0.4.1",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"typescript": "^5.1.3"
},
"resolutions": {

View File

@@ -7,8 +7,5 @@ module.exports = {
trailingComma: 'es5',
bracketSpacing: true,
endOfLine: 'auto',
plugins: ['prettier-plugin-tailwindcss', '@trivago/prettier-plugin-sort-imports'],
importOrder: ['^@core/(.*)$', '^@server/(.*)$', '^@ui/(.*)$', '^[./]'],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
plugins: ['prettier-plugin-tailwindcss'],
}

View File

@@ -24,6 +24,7 @@ module.exports = {
extend: {
screens: {
'bento-sm': '374px',
xs: '475px',
'bento-md': '799px',
'bento-lg': '1199px',
},