mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-02-12 15:52:47 +00:00
feat(app): add book cover image and highlights to BookHomePage
This commit is contained in:
BIN
public/book-cover-photo.jpg
Normal file
BIN
public/book-cover-photo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
BIN
public/book-cover.jpg
Normal file
BIN
public/book-cover.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 226 KiB |
@@ -1,6 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import { parts } from "@/lib/book/chapters";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { ArrowRight, BookOpen, Sparkles, Brain, Layers, Target, Lightbulb } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
@@ -10,54 +10,105 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
export default function BookHomePage() {
|
||||
const highlights = [
|
||||
{ icon: Brain, text: "Understanding how AI models think and process prompts" },
|
||||
{ icon: Target, text: "Crafting clear, specific, and effective prompts" },
|
||||
{ icon: Layers, text: "Advanced techniques: chain-of-thought, few-shot learning, and prompt chaining" },
|
||||
{ icon: Sparkles, text: "Interactive examples you can try directly in the browser" },
|
||||
{ icon: Lightbulb, text: "Real-world use cases for writing, coding, education, and business" },
|
||||
{ icon: BookOpen, text: "The future of prompting: agents and agentic systems" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
<div className="max-w-2xl">
|
||||
{/* Book Cover Image */}
|
||||
<div className="mb-10">
|
||||
<div className="relative aspect-video rounded-lg overflow-hidden shadow-2xl">
|
||||
<Image
|
||||
src="/book-cover-photo.jpg"
|
||||
alt="The Interactive Book of Prompting"
|
||||
fill
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Book Cover Header */}
|
||||
<div className="mb-10">
|
||||
<p className="text-sm text-muted-foreground mb-4">An Interactive Guide by</p>
|
||||
<h2 className="text-lg font-medium mb-6">Fatih Kadir Akın</h2>
|
||||
<h1 className="text-4xl md:text-5xl font-bold tracking-tight mb-4">
|
||||
The Interactive Book of Prompting
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
<p className="text-xl text-muted-foreground">
|
||||
An Interactive Guide to Crafting Clear and Effective Prompts
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Author Introduction */}
|
||||
<div className="mb-10 text-muted-foreground space-y-4">
|
||||
<p>
|
||||
Hi, I'm <strong className="text-foreground">Fatih Kadir Akın</strong>, the curator of the popular{" "}
|
||||
<a href="https://github.com/f/awesome-chatgpt-prompts" className="text-primary hover:underline">
|
||||
Awesome ChatGPT Prompts
|
||||
</a>{" "}
|
||||
repository on GitHub and <strong className="text-foreground">prompts.chat</strong>.
|
||||
</p>
|
||||
<p>
|
||||
In this comprehensive and interactive guide, you'll discover expert strategies for crafting
|
||||
compelling AI prompts that drive engaging and effective conversations. From understanding
|
||||
how AI models work to mastering advanced techniques like prompt chaining and agentic systems,
|
||||
this book provides you with the tools you need to take your AI interactions to the next level.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Highlights */}
|
||||
<div className="mb-10">
|
||||
<h3 className="text-sm font-semibold text-foreground mb-4">What you'll learn:</h3>
|
||||
<div className="space-y-3">
|
||||
{highlights.map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<item.icon className="h-5 w-5 text-primary mt-0.5 shrink-0" />
|
||||
<span className="text-muted-foreground">{item.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Book Structure */}
|
||||
<div className="mb-10 p-6 bg-muted/30 rounded-lg">
|
||||
<h3 className="text-sm font-semibold text-foreground mb-3">Book Structure</h3>
|
||||
<div className="grid grid-cols-2 gap-2 text-sm text-muted-foreground">
|
||||
<div>• Introduction</div>
|
||||
<div>• Part 1: Foundations</div>
|
||||
<div>• Part 2: Techniques</div>
|
||||
<div>• Part 3: Advanced Strategies</div>
|
||||
<div>• Part 4: Best Practices</div>
|
||||
<div>• Part 5: Use Cases</div>
|
||||
<div>• Part 6: Conclusion</div>
|
||||
<div>• 25 Interactive Chapters</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="mb-8">
|
||||
<Button asChild>
|
||||
<div className="mb-10 flex flex-col sm:flex-row gap-3">
|
||||
<Button asChild size="lg">
|
||||
<Link href="/book/00a-preface">
|
||||
Start Reading
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg">
|
||||
<Link href="/book/01-understanding-ai-models">
|
||||
Skip to Chapter 1
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Table of Contents */}
|
||||
<div className="space-y-6">
|
||||
{parts.map((part) => (
|
||||
<section key={part.slug}>
|
||||
<h2 className="text-sm font-semibold text-foreground mb-3">
|
||||
{part.number === 0 ? part.title : `Part ${part.number}: ${part.title}`}
|
||||
</h2>
|
||||
<div className="space-y-1">
|
||||
{part.chapters.map((chapter) => (
|
||||
<Link
|
||||
key={chapter.slug}
|
||||
href={`/book/${chapter.slug}`}
|
||||
className="group flex items-center gap-3 py-2 px-3 -mx-3 rounded-md hover:bg-accent transition-colors"
|
||||
>
|
||||
<span className="text-xs font-mono text-muted-foreground w-6">
|
||||
{String(chapter.chapterNumber).padStart(2, "0")}
|
||||
</span>
|
||||
<span className="flex-1 text-sm text-muted-foreground group-hover:text-foreground transition-colors">
|
||||
{chapter.title}
|
||||
</span>
|
||||
<ArrowRight className="h-3 w-3 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
{/* Note */}
|
||||
<div className="text-sm text-muted-foreground italic">
|
||||
<p>This book is continuously updated with new techniques and insights as AI evolves.</p>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
ExternalLink,
|
||||
Chromium,
|
||||
Hammer,
|
||||
BookOpen,
|
||||
} from "lucide-react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -194,6 +195,14 @@ export function Header({ authProvider = "credentials", allowRegistration = true
|
||||
>
|
||||
{t("nav.promptmasters")}
|
||||
</Link>
|
||||
<Link
|
||||
href="/book"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
|
||||
>
|
||||
<BookOpen className="h-4 w-4" />
|
||||
Book
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -317,6 +326,14 @@ export function Header({ authProvider = "credentials", allowRegistration = true
|
||||
|
||||
{/* Right side actions */}
|
||||
<div className="flex items-center gap-1">
|
||||
{/* Book link */}
|
||||
<Button asChild variant="ghost" size="sm" className="hidden lg:flex h-8 gap-1.5">
|
||||
<Link href="/book">
|
||||
<BookOpen className="h-4 w-4" />
|
||||
Book
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
{/* Developers link */}
|
||||
<Button asChild variant="ghost" size="icon" className="hidden lg:flex h-8 w-8">
|
||||
<Link href="/developers" title={t("nav.developers")}>
|
||||
|
||||
@@ -20,6 +20,11 @@ export function WidgetCard({ prompt }: WidgetCardProps) {
|
||||
const tCommon = useTranslations("common");
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
// If widget has a custom render function, use it
|
||||
if (prompt.render) {
|
||||
return <>{prompt.render()}</>;
|
||||
}
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await navigator.clipboard.writeText(prompt.content);
|
||||
setCopied(true);
|
||||
|
||||
169
src/lib/plugins/widgets/book.tsx
Normal file
169
src/lib/plugins/widgets/book.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { ArrowRight, BookOpen } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { WidgetPlugin } from "./types";
|
||||
|
||||
function BookWidget() {
|
||||
const BOOK_WIDTH = 180;
|
||||
const BOOK_HEIGHT = 260;
|
||||
const BOOK_DEPTH = 22;
|
||||
|
||||
return (
|
||||
<div className="group border rounded-[var(--radius)] overflow-hidden hover:border-foreground/20 transition-colors bg-gradient-to-br from-primary/5 via-background to-primary/10 p-5">
|
||||
<style>{`
|
||||
@keyframes bookFlip {
|
||||
0% { transform: rotateY(0deg); }
|
||||
30% { transform: rotateY(18deg); }
|
||||
50% { transform: rotateY(18deg); }
|
||||
80% { transform: rotateY(-18deg); }
|
||||
100% { transform: rotateY(-18deg); }
|
||||
}
|
||||
@keyframes bookReturn {
|
||||
0% { transform: rotateY(-18deg); }
|
||||
100% { transform: rotateY(0deg); }
|
||||
}
|
||||
@keyframes lightGlow {
|
||||
0% { opacity: 0; }
|
||||
30% { opacity: 0.25; }
|
||||
50% { opacity: 0.2; }
|
||||
80% { opacity: 0.35; }
|
||||
100% { opacity: 0.3; }
|
||||
}
|
||||
@keyframes lightFade {
|
||||
0% { opacity: 0.3; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
.book-3d-anim {
|
||||
animation: bookReturn 0.5s ease-out forwards;
|
||||
}
|
||||
.book-3d-anim:hover {
|
||||
animation: bookFlip 2.5s ease-in-out forwards;
|
||||
}
|
||||
.light-anim {
|
||||
animation: lightFade 0.5s ease-out forwards;
|
||||
}
|
||||
.light-anim:hover {
|
||||
animation: lightGlow 2.5s ease-in-out forwards;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{/* 3D Book Container */}
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
{/* Perspective container */}
|
||||
<Link
|
||||
href="/book"
|
||||
className="block"
|
||||
style={{ perspective: "800px" }}
|
||||
>
|
||||
{/* 3D transform container */}
|
||||
<div
|
||||
className="book-3d-anim relative"
|
||||
style={{
|
||||
width: BOOK_WIDTH,
|
||||
height: BOOK_HEIGHT,
|
||||
transformStyle: "preserve-3d",
|
||||
}}
|
||||
>
|
||||
{/* FRONT: Book Cover */}
|
||||
<div className="absolute inset-0 rounded-sm shadow-xl overflow-hidden group-hover:shadow-2xl transition-shadow duration-300">
|
||||
<Image
|
||||
src="/book-cover.jpg"
|
||||
alt="The Interactive Book of Prompting"
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
{/* Subtle radial light glow from top-right */}
|
||||
<div
|
||||
className="light-anim absolute inset-0 pointer-events-none"
|
||||
style={{
|
||||
background: "radial-gradient(ellipse at 85% 15%, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0.1) 30%, transparent 60%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Drop shadow under book */}
|
||||
<div
|
||||
className="absolute -bottom-3 left-1/2 -translate-x-1/2 w-24 h-4 bg-black/20 blur-md rounded-full opacity-50 group-hover:opacity-80 group-hover:w-28 group-hover:blur-lg transition-all duration-300"
|
||||
/>
|
||||
|
||||
{/* RIGHT: Pages edge - extends backward from cover's right */}
|
||||
<div
|
||||
className="absolute top-0"
|
||||
style={{
|
||||
width: BOOK_DEPTH,
|
||||
height: BOOK_HEIGHT,
|
||||
right: 0,
|
||||
transform: "rotateY(-90deg)",
|
||||
transformOrigin: "right center",
|
||||
background: "repeating-linear-gradient(to bottom, #f8f8f8 0px, #e0e0e0 1px, #f8f8f8 2px)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* LEFT: Spine edge - extends backward from cover's left */}
|
||||
<div
|
||||
className="absolute top-0 rounded-l-sm"
|
||||
style={{
|
||||
width: BOOK_DEPTH,
|
||||
height: BOOK_HEIGHT,
|
||||
left: 0,
|
||||
transform: "rotateY(90deg)",
|
||||
transformOrigin: "left center",
|
||||
background: "linear-gradient(to right, #1a3535, #234848)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Content */}
|
||||
<div className="w-full text-center">
|
||||
<div className="flex items-center justify-center gap-2 mb-2">
|
||||
<BookOpen className="h-4 w-4 text-primary" />
|
||||
<span className="text-xs font-medium text-primary">Free Interactive Guide</span>
|
||||
</div>
|
||||
<h3 className="font-semibold text-base mb-1.5">
|
||||
The Interactive Book of Prompting
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground mb-4">
|
||||
Master AI prompting with 25 interactive chapters.
|
||||
</p>
|
||||
<Button asChild size="sm" className="w-full">
|
||||
<Link href="/book">
|
||||
Start Reading
|
||||
<ArrowRight className="ml-2 h-3.5 w-3.5" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const bookWidget: WidgetPlugin = {
|
||||
id: "book",
|
||||
name: "The Interactive Book of Prompting",
|
||||
prompts: [
|
||||
{
|
||||
id: "book-promo",
|
||||
slug: "interactive-book-of-prompting",
|
||||
title: "The Interactive Book of Prompting",
|
||||
description: "Master the art of crafting effective AI prompts with our comprehensive interactive guide.",
|
||||
content: "",
|
||||
type: "TEXT",
|
||||
tags: ["Prompting", "AI", "Guide", "Learning"],
|
||||
category: "Education",
|
||||
actionUrl: "/book",
|
||||
actionLabel: "Read the Book",
|
||||
positioning: {
|
||||
position: 7,
|
||||
mode: "repeat",
|
||||
repeatEvery: 60,
|
||||
maxCount: 4,
|
||||
},
|
||||
shouldInject: () => {
|
||||
return true;
|
||||
},
|
||||
render: () => <BookWidget />,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { WidgetPlugin, WidgetPrompt, WidgetContext } from "./types";
|
||||
import { coderabbitWidget } from "./coderabbit";
|
||||
import { bookWidget } from "./book";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
// Registry of all widget plugins
|
||||
const widgetPlugins: WidgetPlugin[] = [
|
||||
coderabbitWidget,
|
||||
bookWidget,
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Widgets Plugin Types
|
||||
// ============================================
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export interface WidgetContext {
|
||||
filters?: {
|
||||
q?: string;
|
||||
@@ -56,6 +58,8 @@ export interface WidgetPrompt {
|
||||
/** Widget positioning configuration */
|
||||
positioning?: WidgetPositionConfig;
|
||||
shouldInject?: (context: WidgetContext) => boolean;
|
||||
/** Custom render function for completely custom widget designs */
|
||||
render?: () => ReactNode;
|
||||
}
|
||||
|
||||
export interface InjectedWidget extends WidgetPrompt {
|
||||
|
||||
Reference in New Issue
Block a user