├── .nvmrc ├── pnpm-workspace.yaml ├── .eslintrc.json ├── public ├── logo.jpg ├── avatar.jpg ├── covers │ └── cover_translation_flow2.jpeg └── manifest.json ├── postcss.config.js ├── prettier.config.js ├── next-env.d.ts ├── src ├── app │ ├── providers.tsx │ ├── metadata.tsx │ ├── sitemap.ts │ ├── manifest.ts │ ├── icon.tsx │ ├── robots.ts │ ├── apple-icon.tsx │ ├── feed.xml │ │ └── route.ts │ ├── page.tsx │ ├── categories │ │ └── [category] │ │ │ └── page.tsx │ ├── error.tsx │ ├── not-found.tsx │ ├── posts │ │ └── [...slug] │ │ │ └── layout.tsx │ └── layout.tsx ├── lib │ ├── constants.ts │ ├── images.ts │ ├── routes.ts │ ├── utils.ts │ ├── posts.ts │ └── metadata.ts ├── types │ └── navigation.ts ├── components │ ├── common │ │ ├── VideoPlayer.tsx │ │ ├── ThemeSwitch.tsx │ │ ├── GradientBackground.tsx │ │ ├── Container.tsx │ │ ├── BlurImage.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── Skeleton.tsx │ │ └── Mdx.tsx │ ├── navigation │ │ ├── NavigationSection.tsx │ │ ├── NavigationFooter.tsx │ │ ├── NavigationHeader.tsx │ │ ├── NavigationProfile.tsx │ │ ├── NavigationItem.tsx │ │ └── Navigation.tsx │ ├── home │ │ ├── AsyncPostSections.tsx │ │ ├── FeaturedSection.tsx │ │ ├── PostList.tsx │ │ ├── PostCard.tsx │ │ ├── Hero.tsx │ │ └── FeaturedPost.tsx │ ├── post │ │ ├── PostContent.tsx │ │ ├── ReadingProgress.tsx │ │ ├── Comments.tsx │ │ ├── PostFilter.tsx │ │ ├── PostHeader.tsx │ │ └── PostFooter.tsx │ ├── ui │ │ ├── button.tsx │ │ └── card.tsx │ ├── category │ │ └── CategoryPageContent.tsx │ └── layout │ │ └── CategoryLayout.tsx └── config │ └── navigation.ts ├── tsconfig.node.json ├── components.json ├── .cursorrules ├── .gitignore ├── .github └── FUNDING.yml ├── next.config.js ├── scripts ├── config.ts └── utils.ts ├── tsconfig.json ├── posts ├── reading │ ├── chat │ │ ├── 20250222_daily_chat.mdx │ │ ├── 20250226_daily_chat.mdx │ │ ├── 20250223_daily_chat.mdx │ │ ├── 20250219_daily_chat.mdx │ │ ├── 20250306_daily_chat.mdx │ │ ├── 20250225_daily_chat.mdx │ │ ├── 20250221_daily_chat.mdx │ │ ├── 20250220_daily_chat.mdx │ │ └── 20250227_daily_chat.mdx │ ├── ai │ │ └── 20250221_thoughts_on_ai_coexistence_evolution.mdx │ └── notes │ │ └── 20251107_bestblogs_weekly_issue_71.mdx ├── ai │ ├── model │ │ ├── 20250218_xai_release_grok3.mdx │ │ ├── 20250312_openai_agents_platform.mdx │ │ └── 20250124_openai_introduce_operator.mdx │ ├── agent │ │ ├── 20250213_what_is_a_cognitive_architecture.mdx │ │ ├── 20250123_what_is_ai_agent.mdx │ │ ├── 20250316_speeding_up_llm_powered_agents.mdx │ │ ├── 20250214_outsource_agentic_infrastructure_own_cognitive_architecture.mdx │ │ └── 20250302_memory_for_agents.mdx │ └── mcp │ │ └── 20250311_mcp_fad_or_fixture.mdx ├── thoughts │ └── learning │ │ └── 20251026_campus_interview_insights_from_interviewer.mdx ├── dev │ └── growth │ │ ├── 20251007_how_to_influence_politics.mdx │ │ └── 20240409_technical_project_manager.mdx └── build │ ├── wenrun │ └── 20250308_wenrun_launch.mdx │ └── design │ └── 20250314_practical_ux_for_startups_surviving.mdx ├── documents ├── tasks.md ├── design.md ├── SEO.md ├── coverPrompt.md ├── project.md └── chinese-copywriting-guidelines.md ├── package.json ├── contentlayer.config.ts ├── tailwind.config.js ├── AGENTS.md └── CLAUDE.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "." -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /public/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ginobefun/ginonotes-blog/HEAD/public/logo.jpg -------------------------------------------------------------------------------- /public/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ginobefun/ginonotes-blog/HEAD/public/avatar.jpg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/covers/cover_translation_flow2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ginobefun/ginonotes-blog/HEAD/public/covers/cover_translation_flow2.jpeg -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Options} */ 2 | module.exports = { 3 | singleQuote: true, 4 | semi: false, 5 | plugins: ['prettier-plugin-tailwindcss'], 6 | }; 7 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import "./.next/dev/types/routes.d.ts"; 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 7 | -------------------------------------------------------------------------------- /src/app/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ThemeProvider as NextThemesProvider, type ThemeProviderProps } from 'next-themes' 4 | 5 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 6 | return {children} 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "target": "ES2020", 7 | "sourceMap": true, 8 | "outDir": "dist" 9 | }, 10 | "include": ["scripts/**/*"], 11 | "exclude": ["node_modules"] 12 | } -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/app/global.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/metadata.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from 'next' 2 | import { metadata as siteMetadata } from '@/lib/metadata' 3 | 4 | export const metadata: Metadata = { 5 | ...siteMetadata, 6 | icons: { 7 | icon: '/icon', 8 | shortcut: '/icon', 9 | }, 10 | manifest: '/manifest.webmanifest', 11 | viewport: { 12 | width: 'device-width', 13 | initialScale: 1, 14 | maximumScale: 1, 15 | userScalable: false, 16 | } 17 | } -------------------------------------------------------------------------------- /src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const WEBSITE_HOST_URL = 2 | process.env.NEXT_PUBLIC_WEBSITE_URL || 'https://ginonotes.com' 3 | 4 | export const WEBSITE_NAME = 'Gino Notes' 5 | export const WEBSITE_DESCRIPTION = 6 | '记录学习和思考的内容,分享技术、人工智能、产品设计和生活随想。' 7 | 8 | export const WEBSITE_AUTHOR = 'Gino' 9 | export const WEBSITE_TWITTER = '@hongming731' 10 | export const WEBSITE_LANGUAGE = 'zh-CN' 11 | export const WEBSITE_OG_IMAGE = '/images/og.png' 12 | -------------------------------------------------------------------------------- /.cursorrules: -------------------------------------------------------------------------------- 1 | 这是我个人的博客网站 ginonotes.com,基于 Next.js 14 + Tailwind CSS + contentlayer 构建。请遵循以下规则: 2 | 3 | 1. 确保网站的风格和布局一致,保持清新、整洁、科技的风格,保证读者的阅读体验最佳。 4 | 2. 请遵循 Next.js 14、React 18、TypeScript、Tailwind CSS、contentlayer 的官方文档和最佳实践。 5 | 3. 请保持代码的简洁、易读、易维护,增加必要的注释和说明,以便我能够理解和维护。 6 | 4. 在 posts 目录下,请按照日期和标题的格式来命名文件,例如 20240101_my_first_post.mdx。 7 | 5. 在编写或修改文章时,请使用 Markdown 格式,并遵循 Markdown 的语法和规范,不要随意删除原有的内容。 8 | 6. 请使用中文进行写作,并使用中文标点符号。注意在中文和英文、数字之间增加一个空格。 9 | 7. 优先使用 Tailwind CSS 来编写样式,尽量不要使用原生 CSS。需要考虑网站的响应式设计,特别是在网页端和手机端的阅读体验。 10 | -------------------------------------------------------------------------------- /src/app/sitemap.ts: -------------------------------------------------------------------------------- 1 | import { WEBSITE_HOST_URL } from '@/lib/constants' 2 | import { allPosts } from 'contentlayer2/generated' 3 | 4 | export default async function sitemap() { 5 | const posts = allPosts.map((post) => ({ 6 | url: `${WEBSITE_HOST_URL}${post.url}`, 7 | lastModified: post.date, 8 | })) 9 | 10 | const routes = ['', '/about'].map((route) => ({ 11 | url: `${WEBSITE_HOST_URL}${route}`, 12 | lastModified: new Date().toISOString().split('T')[0], 13 | })) 14 | 15 | return [...routes, ...posts] 16 | } 17 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gino Notes", 3 | "short_name": "Gino", 4 | "description": "记录学习和思考的内容,分享技术、人工智能、产品设计和生活随想。", 5 | "start_url": "/", 6 | "display": "standalone", 7 | "background_color": "#ffffff", 8 | "theme_color": "#ffffff", 9 | "icons": [ 10 | { 11 | "src": "/icon", 12 | "sizes": "32x32", 13 | "type": "image/png" 14 | }, 15 | { 16 | "src": "/apple-icon", 17 | "sizes": "180x180", 18 | "type": "image/png" 19 | } 20 | ], 21 | "orientation": "portrait", 22 | "lang": "zh-CN" 23 | } 24 | -------------------------------------------------------------------------------- /src/types/navigation.ts: -------------------------------------------------------------------------------- 1 | import { Route } from 'next' 2 | import { IconType } from 'react-icons' 3 | import { AppRoute, ExternalRoute } from '@/lib/routes' 4 | 5 | export interface NavigationItem { 6 | href: AppRoute | ExternalRoute 7 | label: string 8 | icon: IconType 9 | count?: number 10 | } 11 | 12 | export interface NavigationSection { 13 | title: string 14 | items: NavigationItem[] 15 | } 16 | 17 | export interface NavigationConfig { 18 | main: NavigationItem[] 19 | posts: NavigationItem[] 20 | projects: NavigationItem[] 21 | online: NavigationItem[] 22 | } -------------------------------------------------------------------------------- /src/app/manifest.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from 'next' 2 | import { metadata } from '@/lib/metadata' 3 | 4 | export default function manifest(): MetadataRoute.Manifest { 5 | return { 6 | name: metadata.title?.toString(), 7 | short_name: 'Gino Notes', 8 | description: metadata.description?.toString(), 9 | start_url: '/', 10 | display: 'standalone', 11 | background_color: '#ffffff', 12 | theme_color: '#ffffff', 13 | icons: [ 14 | { 15 | src: '/icon', 16 | sizes: '32x32', 17 | type: 'image/png' 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | .env 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # contentlayer 39 | .contentlayer 40 | 41 | # Media management 42 | .env 43 | backups/ -------------------------------------------------------------------------------- /src/app/icon.tsx: -------------------------------------------------------------------------------- 1 | import { ImageResponse } from 'next/og' 2 | 3 | // 路由段配置 4 | export const runtime = 'edge' 5 | 6 | // 图片元数据 7 | export const contentType = 'image/png' 8 | 9 | // 生成图标的图像响应 10 | export default function Icon() { 11 | return new ImageResponse( 12 | ( 13 |
26 | G 27 |
28 | ), 29 | { 30 | width: 32, 31 | height: 32, 32 | } 33 | ) 34 | } -------------------------------------------------------------------------------- /src/lib/images.ts: -------------------------------------------------------------------------------- 1 | // 分类映射 2 | export const CATEGORY_MAP = { 3 | dev: '编程技术', 4 | ai: '人工智能', 5 | build: '构建之路', 6 | reading: '阅读记录', 7 | thoughts: '随想思考', 8 | } as const 9 | 10 | // 默认的封面图片 11 | export const DEFAULT_COVERS = { 12 | dev: '/images/covers/dev.jpg', 13 | ai: '/images/covers/ai.jpg', 14 | build: '/images/covers/build.jpg', 15 | reading: '/images/covers/reading.jpg', 16 | thoughts: '/images/covers/thoughts.jpg', 17 | } as const 18 | 19 | // 根据分类获取封面图片 20 | export function getRandomCover(category: keyof typeof DEFAULT_COVERS): string { 21 | return DEFAULT_COVERS[category] || DEFAULT_COVERS.dev 22 | } 23 | 24 | // 获取分类的中文名称 25 | export function getCategoryName(category: keyof typeof CATEGORY_MAP): string { 26 | return CATEGORY_MAP[category] || category 27 | } -------------------------------------------------------------------------------- /src/components/common/VideoPlayer.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | 5 | interface VideoPlayerProps { 6 | src: string 7 | title?: string 8 | className?: string 9 | } 10 | 11 | const VideoPlayer: React.FC = ({ src, title, className = '' }) => { 12 | return ( 13 |
14 |