refactor(bento): rewrite with css grid
This commit is contained in:
@@ -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 })
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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'
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -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>
|
||||
) : (
|
||||
|
||||
@@ -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
46
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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'],
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ module.exports = {
|
||||
extend: {
|
||||
screens: {
|
||||
'bento-sm': '374px',
|
||||
xs: '475px',
|
||||
'bento-md': '799px',
|
||||
'bento-lg': '1199px',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user