Compare commits
7 Commits
33053b4a92
...
88a017d6da
| Author | SHA1 | Date | |
|---|---|---|---|
| 88a017d6da | |||
| a718a5487a | |||
| a04227016e | |||
| 12724bea7f | |||
| 720ca56eb3 | |||
| 8c01303c6c | |||
| 5e2e18fce6 |
@@ -19,7 +19,7 @@ export function BlogContent({ content }: { content?: string }) {
|
||||
h3: ({ ...props }) => <h4 className="text-xl font-bold py-0.5" {...props} />,
|
||||
h4: ({ ...props }) => <h5 className="text-lg font-bold" {...props} />,
|
||||
h5: ({ ...props }) => <h6 className="text-md font-bold" {...props} />,
|
||||
p: ({ ...props }) => <p className="py-1 text-zinc-700" {...props} />,
|
||||
p: ({ ...props }) => <p className="py-1 text-zinc-700 dark:text-zinc-300" {...props} />,
|
||||
img: ({ src }) => (
|
||||
<PhotoProvider className="w-full">
|
||||
<PhotoView src={src as string}>
|
||||
@@ -29,8 +29,8 @@ export function BlogContent({ content }: { content?: string }) {
|
||||
</PhotoView>
|
||||
</PhotoProvider>
|
||||
),
|
||||
th: ({ ...props }) => <th className="text-ellipsis text-nowrap border border-zinc-300 p-2" {...props} />,
|
||||
td: ({ ...props }) => <td className="border border-zinc-300 p-1" {...props} />,
|
||||
th: ({ ...props }) => <th className="text-ellipsis text-nowrap border border-zinc-300 dark:border-zinc-500 p-2" {...props} />,
|
||||
td: ({ ...props }) => <td className="border border-zinc-300 dark:border-zinc-500 p-1" {...props} />,
|
||||
table: ({ ...props }) => <div className="overflow-x-auto"><table {...props} /></div>,
|
||||
pre: ({ ...props }) => <pre className="rounded-sm overflow-hidden shadow" {...props} />,
|
||||
blockquote: ({ ...props }) => <blockquote className="pl-3 border-l-5" {...props} />,
|
||||
|
||||
@@ -37,7 +37,7 @@ export function BlogComments({ blogId }: { blogId: string }) {
|
||||
handleClearReplayTarget={() => setReplayTarget(null)}
|
||||
/>
|
||||
|
||||
<div className="text-sm text-zinc-600">
|
||||
<div className="text-sm text-zinc-600 dark:text-zinc-400">
|
||||
{
|
||||
user ? (<span>当前账户:{user.nickname}</span>) : (<span>当前未登录,留言名称为匿名,登录可前往控制台</span>)
|
||||
}
|
||||
@@ -47,21 +47,21 @@ export function BlogComments({ blogId }: { blogId: string }) {
|
||||
{
|
||||
data.filter(d => !d.parentId)
|
||||
.map((d) => (
|
||||
<div key={d.id} className="border-b border-zinc-300 py-2 last:border-none">
|
||||
<h1 className="text-zinc-500">{d.user ? d.user.nickname : '匿名'}</h1>
|
||||
<div key={d.id} className="border-b border-zinc-300 dark:border-zinc-500 py-2 last:border-none">
|
||||
<h1 className="text-zinc-500 dark:text-zinc-200">{d.user ? d.user.nickname : '匿名'}</h1>
|
||||
<div className="whitespace-pre-wrap break-all">{d.content}</div>
|
||||
<div className="text-xs text-zinc-500 flex gap-2">
|
||||
<p>{new Date(d.createdAt).toLocaleString()}</p>
|
||||
<p>{d.address}</p>
|
||||
<p className="text-zinc-900 cursor-pointer" onClick={() => setReplayTarget(d)}>回复</p>
|
||||
<p className="text-zinc-900 dark:text-zinc-200 cursor-pointer" onClick={() => setReplayTarget(d)}>回复</p>
|
||||
</div>
|
||||
{
|
||||
data.filter(c => c.parentId === d.id).length > 0 && (
|
||||
<div className="flex flex-col ml-5 my-1">
|
||||
{
|
||||
data.filter(c => c.parentId === d.id).map(c => (
|
||||
<div key={c.id} className="border-b border-zinc-300 py-1 last:border-none">
|
||||
<h1 className="text-zinc-500">{c.user ? c.user.nickname : '匿名'}</h1>
|
||||
<div key={c.id} className="border-b border-zinc-300 dark:border-zinc-500 py-1 last:border-none">
|
||||
<h1 className="text-zinc-500 dark:text-zinc-200">{c.user ? c.user.nickname : '匿名'}</h1>
|
||||
<div className="whitespace-pre-wrap break-all">{c.content}</div>
|
||||
<div className="text-xs text-zinc-500 flex gap-2">
|
||||
<p>{new Date().toLocaleString()}</p>
|
||||
|
||||
@@ -78,12 +78,12 @@ export default async function Page({ params, searchParams }: PageRouteProps) {
|
||||
return (
|
||||
<div className="w-full overflow-x-hidden">
|
||||
<div className="max-w-200 mx-auto px-5 overflow-x-hidden mb-10">
|
||||
{errorMsg && <div className="my-20 text-center text-zinc-600">{errorMsg}</div>}
|
||||
{errorMsg && <div className="my-20 text-center text-zinc-600 dark:text-zinc-400">{errorMsg}</div>}
|
||||
{data && (
|
||||
<article className="w-full">
|
||||
<header className="flex flex-col items-center">
|
||||
<h1 className="text-center text-2xl sm:text-3xl font-bold mt-10 transition-all duration-500">{data.title}</h1>
|
||||
<time className="text-sm text-zinc-500 text-center my-2 sm:my-5 mb-5 transition-all duration-500">发布于:{new Date(data.createdAt).toLocaleString()}</time>
|
||||
<time className="text-sm text-zinc-500 dark:text-zinc-300 text-center my-2 sm:my-5 mb-5 transition-all duration-500">发布于:{new Date(data.createdAt).toLocaleString()}</time>
|
||||
</header>
|
||||
<BlogContent content={data.content} />
|
||||
</article>
|
||||
|
||||
@@ -54,11 +54,11 @@ export default async function Blog() {
|
||||
className="hover:underline focus:outline-none focus:ring-2 focus:ring-zinc-400 rounded"
|
||||
href={getBlogDetailUrl(blog.slug)}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
>
|
||||
{blog.title}
|
||||
</a>
|
||||
</h2>
|
||||
<p className="text-sm font-medium text-zinc-600">{blog.description}</p>
|
||||
<p className="text-sm font-medium text-zinc-600 dark:text-zinc-300">{blog.description}</p>
|
||||
<footer className="mt-3 text-sm text-zinc-500 flex items-center gap-2">
|
||||
<time dateTime={blog.createdAt}>
|
||||
{new Date(blog.createdAt).toLocaleString('zh-CN')}
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function LayoutWithHeaderFooter({
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className="flex-1 flex flex-col bg-zinc-50">
|
||||
<main className="flex-1 flex flex-col bg-zinc-50 dark:bg-zinc-950">
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
|
||||
@@ -11,11 +11,10 @@ export default function Home() {
|
||||
height={180}
|
||||
className="rounded-full duration-400 size-35 md:size-45 select-none"
|
||||
priority
|
||||
quality={100}
|
||||
/>
|
||||
</figure>
|
||||
<h1 className='text-4xl md:text-5xl font-bold mt-5 md:mt-8 gradient-title duration-400 select-none'>特恩(TONE)</h1>
|
||||
<p className='text-lg sm:text-xl md:text-2xl mt-3 font-medium text-zinc-400 duration-400 select-none'>一名在各个领域反复横跳的程序员</p>
|
||||
<p className='text-lg sm:text-xl md:text-2xl mt-3 font-medium text-zinc-400 dark:text-zinc-200 duration-400 select-none'>一名在各个领域反复横跳的程序员</p>
|
||||
<nav className='flex sm:flex-row flex-col gap-2 sm:gap-10 mt-5 md:mt-8 duration-400' aria-label="社交媒体链接">
|
||||
<a href='https://space.bilibili.com/474156211'
|
||||
target='_black'
|
||||
|
||||
@@ -18,7 +18,7 @@ export function ResourceCard({ r, ...props }: ResourceCardProps) {
|
||||
</div>
|
||||
<div className="flex-1 overflow-x-hidden">
|
||||
<div className="font-bold text-2xl">{r.title}</div>
|
||||
<div className="font-medium text-sm text-zinc-400 mt-1">{r.description}</div>
|
||||
<div className="font-medium text-sm text-zinc-400 dark:text-zinc-300 mt-1">{r.description}</div>
|
||||
<div className="flex gap-2 flex-wrap mt-4">
|
||||
{
|
||||
r.tags.map((tag) => (
|
||||
|
||||
@@ -16,9 +16,9 @@ export default function ResourceCardImage({ imageUrl }: ResourceCardImage) {
|
||||
alt="资源图片"
|
||||
width={90}
|
||||
height={90}
|
||||
className="rounded-md shadow"
|
||||
className="rounded-md shadow w-22.5 h-22.5"
|
||||
priority
|
||||
quality={80}
|
||||
quality={75}
|
||||
onError={() => setImageError(true)}
|
||||
/>}
|
||||
</>
|
||||
|
||||
@@ -18,9 +18,9 @@ export default async function Resources() {
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col items-center">
|
||||
<h1 className="mt-6 md:mt-20 text-2xl md:text-5xl font-medium text-zinc-800 text-center duration-300">精心挑选并收藏的资源</h1>
|
||||
<p className="mt-4 md:mt-8 mx-3 text-zinc-400 text-sm text-center duration-300">请在浏览此部分内容前阅读并同意
|
||||
<a className="text-zinc-600">《使用条款和隐私政策》</a>
|
||||
<h1 className="mt-6 md:mt-20 text-2xl md:text-5xl font-medium text-zinc-800 dark:text-zinc-200 text-center duration-300">精心挑选并收藏的资源</h1>
|
||||
<p className="mt-4 md:mt-8 mx-3 text-zinc-400 dark:text-zinc-300 text-sm text-center duration-300">请在浏览此部分内容前阅读并同意
|
||||
<a className="text-zinc-600 dark:text-zinc-400">《使用条款和隐私政策》</a>
|
||||
,继续使用或浏览表示您接受协议条款。</p>
|
||||
{
|
||||
errorMsg && (
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function Login() {
|
||||
alt="Image"
|
||||
width={500}
|
||||
height={500}
|
||||
className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
|
||||
className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.6] dark:grayscale"
|
||||
priority
|
||||
quality={100}
|
||||
/>
|
||||
|
||||
@@ -10,19 +10,19 @@ const EMAIL = "tonesc.cn@gmail.com";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-zinc-300">
|
||||
<div className="bg-zinc-50 px-4 py-3 md:py-5 sm:px-10 md:px-20 flex flex-col sm:flex-row justify-between items-center gap-4 transition-all">
|
||||
<footer className="border-t border-zinc-300 dark:border-zinc-500">
|
||||
<div className="bg-zinc-50 dark:bg-zinc-950 px-4 py-3 md:py-5 sm:px-10 md:px-20 flex flex-col sm:flex-row justify-between items-center gap-4 transition-all">
|
||||
{/* 版权与备案信息 */}
|
||||
<div className="text-center sm:text-left">
|
||||
<a
|
||||
href="https://beian.miit.gov.cn/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block text-sm text-zinc-500 hover:text-zinc-700 hover:underline focus:outline-none focus:underline"
|
||||
className="block text-sm text-zinc-500 dark:text-zinc-300 hover:text-zinc-700 dark:hover:text-zinc-100 hover:underline focus:outline-none focus:underline"
|
||||
>
|
||||
备案号:渝ICP备2023009516号-1
|
||||
</a>
|
||||
<p className="mt-1 text-sm text-zinc-500">
|
||||
<p className="mt-1 text-sm text-zinc-500 dark:text-zinc-300">
|
||||
© {new Date().getFullYear()} TONE Page. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
@@ -32,13 +32,13 @@ export default function Footer() {
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant='outline' size='sm' >
|
||||
<Mail className="text-zinc-600" />
|
||||
<Mail className="text-zinc-600 dark:text-zinc-300" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-fit">
|
||||
<a
|
||||
href={`mailto:${EMAIL}`}
|
||||
className="text-sm text-zinc-800 hover:underline focus:outline-none focus:underline"
|
||||
className="text-sm text-zinc-800 dark:text-zinc-200 hover:underline focus:outline-none focus:underline"
|
||||
>
|
||||
{EMAIL}
|
||||
</a>
|
||||
|
||||
@@ -48,13 +48,13 @@ export default function Header() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="sticky top-0 z-50 backdrop-blur-sm bg-white/40 shadow" role="banner" aria-label="网站顶部导航栏">
|
||||
<header className="sticky top-0 z-50 backdrop-blur-sm bg-white/40 dark:bg-black/40 shadow dark:shadow-zinc-500" role="banner" aria-label="网站顶部导航栏">
|
||||
<div className="flex items-center justify-between px-10 md:h-18 md:px-20 h-14 duration-300" aria-label="主菜单">
|
||||
<Link
|
||||
href="/"
|
||||
className={cn(
|
||||
"cursor-pointer font-medium text-zinc-500 hover:text-zinc-800 border-b-4 border-transparent duration-200",
|
||||
pathname === "/" && "text-zinc-800"
|
||||
"cursor-pointer font-medium text-zinc-500 dark:text-zinc-300 hover:text-zinc-800 dark:hover:text-zinc-100 border-b-4 border-transparent duration-200",
|
||||
pathname === "/" && "text-zinc-800 dark:text-zinc-100"
|
||||
)}
|
||||
aria-current={pathname === "/" ? "page" : undefined}
|
||||
>
|
||||
@@ -72,8 +72,8 @@ export default function Header() {
|
||||
key={item.name}
|
||||
href={item.path}
|
||||
className={cn(
|
||||
"cursor-pointer md:text-lg font-medium text-zinc-500 hover:text-zinc-800 border-b-4 border-transparent duration-200",
|
||||
pathname.startsWith(item.path) && "text-zinc-800 border-b-pink-500"
|
||||
"cursor-pointer md:text-lg font-medium text-zinc-500 dark:text-zinc-300 hover:text-zinc-800 dark:hover:text-zinc-100 border-b-4 border-transparent duration-200",
|
||||
pathname.startsWith(item.path) && "text-zinc-800 dark:text-zinc-100 border-b-pink-500"
|
||||
)}
|
||||
onClick={e => handleClick(e, item.path)}
|
||||
aria-current={pathname === item.path ? "page" : undefined}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
interface ResourceBadgeProps extends React.HTMLProps<HTMLDivElement> {
|
||||
@@ -10,23 +11,17 @@ export function ResourceBadge({ tag, editMode, onClose, ...props }: ResourceBadg
|
||||
return (
|
||||
<div
|
||||
id={tag.name}
|
||||
className="text-[10px] text-zinc-500 font-medium py-[1px] px-1.5 rounded-full flex items-center gap-1"
|
||||
style={{
|
||||
backgroundColor: (() => {
|
||||
switch (tag.type) {
|
||||
case 'os':
|
||||
return '#dbedfd';
|
||||
default:
|
||||
return '#e4e4e7';
|
||||
}
|
||||
})()
|
||||
}}
|
||||
className={cn(
|
||||
"text-[10px] text-zinc-500 dark:text-zinc-300 dark:border font-medium py-px px-1.5 rounded-full flex items-center gap-1",
|
||||
'bg-[#e4e4e7] dark:bg-[#2d2d30]',
|
||||
tag.type === 'os' || 'bg-[#dbedfd] dark:bg-[#1e3a5f]',
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="text-nowrap">{tag.name}</span>
|
||||
{
|
||||
editMode && (
|
||||
<span onClick={() => onClose?.(tag.name)}><X className="w-3 h-3 text-zinc-800 cursor-pointer" /></span>
|
||||
<span onClick={() => onClose?.(tag.name)}><X className="w-3 h-3 text-zinc-800 dark:text-zinc-200 cursor-pointer" /></span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user