Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bun.lockb
Binary file not shown.
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@contentful/rich-text-react-renderer": "^16.0.1",
"@hookform/resolvers": "^4.0.0",
"@million/lint": "^1.0.14",
"@next/third-parties": "15.2.0",
"@next/third-parties": "^15.3.2",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-select": "^2.1.6",
Expand All @@ -39,14 +39,15 @@
"framer-motion": "^12.4.7",
"geist": "^1.3.1",
"isbot": "^5.1.22",
"lodash.throttle": "^4.1.1",
"lru-cache": "^11.0.2",
"lucide-react": "^0.475.0",
"markdown-to-jsx": "^7.7.4",
"million": "^3.1.11",
"next": "15.2.4",
"react": "19.0.0",
"react-dom": "19.0.0",
"next": "^15.3.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-hook-form": "^7.54.2",
"react-icons": "^5.5.0",
"react-intersection-observer": "^9.15.1",
"react-tweet": "^3.2.2",
"react-wrap-balancer": "^1.1.1",
Expand All @@ -61,9 +62,9 @@
"@eslint/compat": "^1.2.7",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.20.0",
"@tailwindcss/postcss": "^4.0.9",
"@tailwindcss/postcss": "^4.1.7",
"eslint": "^9.20.1",
"eslint-config-next": "15.2.0",
"eslint-config-next": "^15.3.2",
"eslint-config-prettier": "^10.0.1",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.31.0",
Expand All @@ -76,7 +77,7 @@
"postcss": "^8.5.3",
"prettier": "^3.5.1",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^4.0.9",
"tailwindcss": "^4.1.7",
"tailwindcss-animate": "^1.0.7"
},
"overrides": {
Expand Down
6 changes: 3 additions & 3 deletions src/app/bookmarks/page.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Link from 'next/link'
import NextLink from 'next/link'
import { Suspense } from 'react'

import { FloatingHeader } from '@/components/floating-header'
Expand All @@ -23,14 +23,14 @@ export default async function Writing() {
<Suspense fallback={<ScreenLoadingSpinner />}>
{bookmarks?.map((bookmark) => {
return (
<Link
<NextLink
key={bookmark._id}
href={`/bookmarks/${bookmark.slug}`}
className="flex flex-col gap-1 border-b px-4 py-3 text-sm hover:bg-gray-100"
>
<span className="font-medium">{bookmark.title}</span>
<span className="text-slate-500">{bookmark.count} bookmarks</span>
</Link>
</NextLink>
)
})}
</Suspense>
Expand Down
4 changes: 2 additions & 2 deletions src/app/journey/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ export default async function Journey() {
{item.logs.map((log, logIndex) => (
<div key={`data_${itemIndex}_log_${logIndex}`} className="relative flex pb-8 last:pb-0">
{logIndex !== item.logs.length - 1 && (
<div className="absolute inset-0 flex w-5 items-center justify-center">
<div className="absolute inset-0 top-0.5 flex w-5 items-center justify-center">
<div className="pointer-events-none h-full w-px border-l border-dashed border-gray-200"></div>
</div>
)}
<div className="z-0 grid size-5 shrink-0 place-items-center rounded-full border bg-white text-white shadow-xs">
<div className="z-0 mt-0.5 grid size-5 shrink-0 place-items-center rounded-full border bg-white text-white shadow-xs">
<div className="size-2 rounded-full bg-blue-600" />
</div>
<div className="grow pl-4 lg:pl-8">
Expand Down
4 changes: 2 additions & 2 deletions src/app/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import '@/globals.css'
import { SpeedInsights } from '@vercel/speed-insights/next'
import { GeistMono } from 'geist/font/mono'
import { GeistSans } from 'geist/font/sans'
import { EyeIcon } from 'lucide-react'
import { draftMode } from 'next/headers'
import Script from 'next/script'
import { LuEye as EyeIcon } from 'react-icons/lu'

import { sharedMetadata } from '@/app/shared-metadata'
import { MenuContent } from '@/components/menu-content'
Expand Down Expand Up @@ -39,7 +39,7 @@ export default async function RootLayout({ children }) {
</div>
)}
<div className="lg:flex">
<SideMenu className="relative hidden lg:flex">
<SideMenu>
<MenuContent />
</SideMenu>
Comment on lines +44 to 46
Copy link

Copilot AI May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original wrapper passed className="relative hidden lg:flex" to SideMenu. Removing these classes may change its positioning and responsive behavior. Consider reapplying necessary layout classes or moving them into the SideMenu component to preserve the intended layout.

Suggested change
<SideMenu>
<MenuContent />
</SideMenu>
<div className="relative hidden lg:flex">
<SideMenu>
<MenuContent />
</SideMenu>
</div>

Copilot uses AI. Check for mistakes.
<div className="flex flex-1">{children}</div>
Expand Down
6 changes: 3 additions & 3 deletions src/app/page.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Link from 'next/link'
import NextLink from 'next/link'
import { Suspense } from 'react'

import { FloatingHeader } from '@/components/floating-header'
Expand Down Expand Up @@ -36,9 +36,9 @@ export default async function Home() {
at Sistas, Mobile Developer at Tanbula, and Specialist at Apple.
</p>
<Button asChild variant="link" className="inline px-0">
<Link href="/writing">
<NextLink href="/writing">
<h2 className="mt-8 mb-4">Writing</h2>
</Link>
</NextLink>
</Button>
<Suspense fallback={<ScreenLoadingSpinner />}>
<WritingList items={items} header="Writing" />
Expand Down
7 changes: 5 additions & 2 deletions src/components/bookmark-card.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Link2Icon } from 'lucide-react'
import dynamic from 'next/dynamic'
import { LuLink2 as Link2Icon } from 'react-icons/lu'

const TweetCard = dynamic(() => import('@/components/tweet-card/tweet-card').then((mod) => mod.TweetCard))
import { TWEETS_COLLECTION_ID } from '@/lib/constants'
Expand All @@ -11,11 +11,14 @@ export const BookmarkCard = ({ bookmark, order }) => {
return <TweetCard id={tweetId} />
}

const href = new URL(bookmark.link)
href.searchParams.set('ref', 'onur.dev')

return (
<a
key={bookmark._id}
className="thumbnail-shadow flex aspect-auto min-w-0 cursor-pointer flex-col gap-4 overflow-hidden rounded-xl bg-white p-4 transition-colors duration-300 hover:bg-gray-100"
href={`${bookmark.link}?ref=onur.dev`}
href={href.toString()}
target="_blank"
rel="noopener noreferrer"
data-bookmark-order={order}
Expand Down
2 changes: 1 addition & 1 deletion src/components/bookmark-list.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { ArrowDownIcon } from 'lucide-react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { LuArrowDown as ArrowDownIcon } from 'react-icons/lu'

import { getBookmarkItemsByPageIndex } from '@/app/actions'
import { BookmarkCard } from '@/components/bookmark-card'
Expand Down
69 changes: 44 additions & 25 deletions src/components/floating-header.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
'use client'

import { ArrowLeftIcon, RadioIcon } from 'lucide-react'
import throttle from 'lodash.throttle'
import dynamic from 'next/dynamic'
import Link from 'next/link'
import NextLink from 'next/link'
import { usePathname } from 'next/navigation'
import { memo, useEffect, useMemo, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import { LuArrowLeft as ArrowLeftIcon, LuRadio as RadioIcon } from 'react-icons/lu'
import Balancer from 'react-wrap-balancer'

import { LoadingSpinner } from '@/components/loading-spinner'
import { Button } from '@/components/ui/button'

const THROTTLE_DELAY = 16 // 60fps
const BALANCER_RATIO = 0.35
const SCROLL_TRANSLATE_BASE = 100
const SCROLL_OPACITY_DIVISOR = 100

const MobileDrawer = dynamic(() => import('@/components/mobile-drawer').then((mod) => mod.MobileDrawer))
const SubmitBookmarkDrawer = dynamic(
() => import('@/components/submit-bookmark/drawer').then((mod) => mod.SubmitBookmarkDrawer),
Expand All @@ -20,6 +26,19 @@ const SubmitBookmarkDrawer = dynamic(
)
import { MOBILE_SCROLL_THRESHOLD, SCROLL_AREA_ID } from '@/lib/constants'

const calculateOpacity = (scrollY) => {
return Math.min(
Math.max(
(
(scrollY - MOBILE_SCROLL_THRESHOLD * (MOBILE_SCROLL_THRESHOLD / (scrollY ** 2 / SCROLL_OPACITY_DIVISOR))) /
SCROLL_OPACITY_DIVISOR
).toFixed(2),
0
),
1
)
}

export const FloatingHeader = memo(({ scrollTitle, title, goBackLink, bookmarks, currentBookmark, children }) => {
const [transformValues, setTransformValues] = useState({ translateY: 0, opacity: scrollTitle ? 0 : 1 })
const pathname = usePathname()
Expand All @@ -30,31 +49,28 @@ export const FloatingHeader = memo(({ scrollTitle, title, goBackLink, bookmarks,

const memoizedMobileDrawer = useMemo(() => <MobileDrawer />, [])

useEffect(() => {
const scrollAreaElem = document.querySelector(`#${SCROLL_AREA_ID}`)
const onScroll = useCallback((e) => {
const scrollY = e.target.scrollTop
const translateY = Math.max(SCROLL_TRANSLATE_BASE - scrollY, 0)
const opacity = calculateOpacity(scrollY)
setTransformValues({ translateY, opacity })
}, [])

const onScroll = (e) => {
const scrollY = e.target.scrollTop
const throttledOnScroll = useMemo(() => throttle(onScroll, THROTTLE_DELAY), [onScroll])

const translateY = Math.max(100 - scrollY, 0)
const opacity = Math.min(
Math.max(
((scrollY - MOBILE_SCROLL_THRESHOLD * (MOBILE_SCROLL_THRESHOLD / (scrollY ** 2 / 100))) / 100).toFixed(2),
0
),
1
)

setTransformValues({ translateY, opacity })
}
useEffect(() => {
const scrollAreaElem = document.querySelector(`#${SCROLL_AREA_ID}`)

if (scrollTitle) {
scrollAreaElem?.addEventListener('scroll', onScroll, {
scrollAreaElem?.addEventListener('scroll', throttledOnScroll, {
passive: true
})
}
return () => scrollAreaElem?.removeEventListener('scroll', onScroll)
}, [scrollTitle])
return () => {
scrollAreaElem?.removeEventListener('scroll', throttledOnScroll)
throttledOnScroll.cancel() // Clean up throttle
}
}, [scrollTitle, throttledOnScroll])

const memoizedSubmitBookmarkDrawer = useMemo(
() => <SubmitBookmarkDrawer bookmarks={bookmarks} currentBookmark={currentBookmark} />,
Expand All @@ -63,23 +79,26 @@ export const FloatingHeader = memo(({ scrollTitle, title, goBackLink, bookmarks,

const memoizedBalancer = useMemo(
() => (
<Balancer ratio={0.35}>
<Balancer ratio={BALANCER_RATIO}>
<span className="line-clamp-2 font-semibold tracking-tight">{title}</span>
</Balancer>
),
[title]
)

return (
<header className="sticky inset-x-0 top-0 z-10 mx-auto flex h-12 w-full shrink-0 items-center overflow-hidden border-b bg-white text-sm font-medium lg:hidden">
<header
className="sticky inset-x-0 top-0 z-10 mx-auto flex h-12 w-full shrink-0 items-center overflow-hidden border-b bg-white text-sm font-medium lg:hidden"
role="banner"
>
<div className="flex size-full items-center px-3">
<div className="flex w-full items-center justify-between gap-2">
<div className="flex flex-1 items-center gap-1">
{goBackLink ? (
<Button variant="ghost" size="icon" className="shrink-0" asChild>
<Link href={goBackLink} title="Go back">
<NextLink href={goBackLink} title="Go back">
<ArrowLeftIcon size={16} />
</Link>
</NextLink>
</Button>
) : (
memoizedMobileDrawer
Expand Down
5 changes: 4 additions & 1 deletion src/components/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { isExternalLink } from '@/lib/utils'
export const Link = ({ href = '#', ...rest }) => {
const isExternal = isExternalLink(href)
if (isExternal) {
const hrefObj = new URL(href)
hrefObj.searchParams.set('ref', 'onur.dev')

return (
<a
href={href + '?ref=onur.dev'}
href={hrefObj.toString()}
target="_blank"
rel="noopener noreferrer"
className="link break-words after:content-['_↗']"
Expand Down
6 changes: 3 additions & 3 deletions src/components/list-item.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import Link from 'next/link'
import NextLink from 'next/link'
import { usePathname } from 'next/navigation'

import { cn } from '@/lib/utils'
Expand All @@ -10,7 +10,7 @@ export const ListItem = ({ title, description, path }) => {
const isActive = pathname === path

return (
<Link
<NextLink
key={path}
href={path}
className={cn(
Expand All @@ -20,6 +20,6 @@ export const ListItem = ({ title, description, path }) => {
>
<span className={cn('font-medium', isActive && 'text-white')}>{title}</span>
{description && <span className={cn(isActive ? 'text-slate-300' : 'text-slate-500')}>{description}</span>}
</Link>
</NextLink>
)
}
6 changes: 3 additions & 3 deletions src/components/menu-content.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Link from 'next/link'
import NextLink from 'next/link'

import { NavigationLink } from '@/components/navigation-link'
import { LINKS, PROFILES } from '@/lib/constants'

export const MenuContent = () => (
<div className="flex w-full flex-col text-sm">
<div className="flex flex-col gap-4">
<Link href="/" className="link-card inline-flex items-center gap-2 p-2">
<NextLink href="/" className="link-card inline-flex items-center gap-2 p-2">
<img
src="/assets/me.avif"
alt="Onur Şuyalçınkaya"
Expand All @@ -21,7 +21,7 @@ export const MenuContent = () => (
<span className="font-semibold tracking-tight">Onur Şuyalçınkaya</span>
<span className="text-gray-600">Software Engineer</span>
</div>
</Link>
</NextLink>
<div className="flex flex-col gap-1">
{LINKS.map((link, linkIndex) => (
<NavigationLink
Expand Down
2 changes: 1 addition & 1 deletion src/components/mobile-drawer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommandIcon } from 'lucide-react'
import { LuCommand as CommandIcon } from 'react-icons/lu'

import { MenuContent } from '@/components/menu-content'
import { Button } from '@/components/ui/button'
Expand Down
8 changes: 4 additions & 4 deletions src/components/navigation-link.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'

import { ArrowUpRightIcon, AtSignIcon } from 'lucide-react'
import Link from 'next/link'
import NextLink from 'next/link'
import { usePathname } from 'next/navigation'
import { memo, useMemo } from 'react'
import { LuArrowUpRight as ArrowUpRightIcon, LuAtSign as AtSignIcon } from 'react-icons/lu'

import { cn } from '@/lib/utils'

Expand Down Expand Up @@ -37,7 +37,7 @@ export const NavigationLink = memo(({ href, label, icon, shortcutNumber }) => {
}

return (
<Link
<NextLink
key={href}
href={href}
className={cn(
Expand All @@ -60,7 +60,7 @@ export const NavigationLink = memo(({ href, label, icon, shortcutNumber }) => {
{shortcutNumber}
</span>
)}
</Link>
</NextLink>
)
})
NavigationLink.displayName = 'NavigationLink'
2 changes: 1 addition & 1 deletion src/components/side-menu.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use client'

import { RadioIcon } from 'lucide-react'
import dynamic from 'next/dynamic'
import { usePathname, useRouter } from 'next/navigation'
import { useMemo } from 'react'
import { LuRadio as RadioIcon } from 'react-icons/lu'

import { LoadingSpinner } from '@/components/loading-spinner'
import { ScrollArea } from '@/components/scroll-area'
Expand Down
2 changes: 1 addition & 1 deletion src/components/submit-bookmark/dialog.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import { SendIcon } from 'lucide-react'
import { useState } from 'react'
import { LuSend as SendIcon } from 'react-icons/lu'

import { SubmitBookmarkForm } from '@/components/submit-bookmark/form'
import { Button } from '@/components/ui/button'
Expand Down
Loading