44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | {/* Anchor */}
52 |
53 |
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss"
2 |
3 | const config = {
4 | darkMode: ["class"],
5 | content: [
6 | "./pages/**/*.{ts,tsx}",
7 | "./components/**/*.{ts,tsx}",
8 | "./app/**/*.{ts,tsx}",
9 | "./src/**/*.{ts,tsx}",
10 | ],
11 | prefix: "",
12 | theme: {
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: "var(--radius)",
58 | md: "calc(var(--radius) - 2px)",
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | keyframes: {
62 | "accordion-down": {
63 | from: { height: "0" },
64 | to: { height: "var(--radix-accordion-content-height)" },
65 | },
66 | "accordion-up": {
67 | from: { height: "var(--radix-accordion-content-height)" },
68 | to: { height: "0" },
69 | },
70 | "fade-in": {
71 | from: {
72 | opacity: "0",
73 | },
74 | to: { opacity: "1" },
75 | },
76 | "fade-up": {
77 | from: {
78 | opacity: "0",
79 | transform: "translateY(var(--fade-distance, .25rem))",
80 | },
81 | to: { opacity: "1", transform: "translateY(0)" },
82 | },
83 | },
84 | animation: {
85 | "accordion-down": "accordion-down 0.2s ease-out",
86 | "accordion-up": "accordion-up 0.2s ease-out",
87 | "fade-in": "fade-in 0.3s ease-out forwards",
88 | "fade-up": "fade-up 1s ease-out forwards",
89 | },
90 | },
91 | },
92 | plugins: [require("tailwindcss-animate")],
93 | } satisfies Config
94 |
95 | export default config
96 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 240 10% 3.9%;
9 | --card: 0 0% 100%;
10 | --card-foreground: 240 10% 3.9%;
11 | --popover: 0 0% 100%;
12 | --popover-foreground: 240 10% 3.9%;
13 | --primary: 240 5.9% 10%;
14 | --primary-foreground: 0 0% 98%;
15 | --secondary: 240 4.8% 95.9%;
16 | --secondary-foreground: 240 5.9% 10%;
17 | --muted: 240 4.8% 95.9%;
18 | --muted-foreground: 240 3.8% 46.1%;
19 | --accent: 240 4.8% 95.9%;
20 | --accent-foreground: 240 5.9% 10%;
21 | --destructive: 0 72.22% 50.59%;
22 | --destructive-foreground: 0 0% 98%;
23 | --border: 240 5.9% 90%;
24 | --input: 240 5.9% 90%;
25 | --ring: 240 5% 64.9%;
26 | --radius: 0.5rem;
27 | }
28 |
29 | .dark {
30 | --background: 240 10% 3.9%;
31 | --foreground: 0 0% 98%;
32 | --card: 240 10% 3.9%;
33 | --card-foreground: 0 0% 98%;
34 | --popover: 240 10% 3.9%;
35 | --popover-foreground: 0 0% 98%;
36 | --primary: 0 0% 98%;
37 | --primary-foreground: 240 5.9% 10%;
38 | --secondary: 240 3.7% 15.9%;
39 | --secondary-foreground: 0 0% 98%;
40 | --muted: 240 3.7% 15.9%;
41 | --muted-foreground: 240 5% 64.9%;
42 | --accent: 240 3.7% 15.9%;
43 | --accent-foreground: 0 0% 98%;
44 | --destructive: 0 62.8% 30.6%;
45 | --destructive-foreground: 0 85.7% 97.3%;
46 | --border: 240 3.7% 15.9%;
47 | --input: 240 3.7% 15.9%;
48 | --ring: 240 4.9% 83.9%;
49 | }
50 | }
51 |
52 | @media (prefers-color-scheme: dark) {
53 | :root {
54 | --background: 240 10% 3.9%;
55 | --foreground: 0 0% 98%;
56 | --card: 240 10% 3.9%;
57 | --card-foreground: 0 0% 98%;
58 | --popover: 240 10% 3.9%;
59 | --popover-foreground: 0 0% 98%;
60 | --primary: 0 0% 98%;
61 | --primary-foreground: 240 5.9% 10%;
62 | --secondary: 240 3.7% 15.9%;
63 | --secondary-foreground: 0 0% 98%;
64 | --muted: 240 3.7% 15.9%;
65 | --muted-foreground: 240 5% 64.9%;
66 | --accent: 240 3.7% 15.9%;
67 | --accent-foreground: 0 0% 98%;
68 | --destructive: 0 62.8% 30.6%;
69 | --destructive-foreground: 0 85.7% 97.3%;
70 | --border: 240 3.7% 15.9%;
71 | --input: 240 3.7% 15.9%;
72 | --ring: 240 4.9% 83.9%;
73 | }
74 | }
75 |
76 | @layer base {
77 | * {
78 | @apply border-border;
79 | }
80 | body {
81 | /* @apply bg-background text-foreground selection:bg-[#6B2BF4] selection:text-foreground; */
82 | @apply bg-background text-foreground;
83 | /* font-feature-settings: "rlig" 1, "calt" 1; */
84 | font-synthesis-weight: none;
85 | text-rendering: optimizeLegibility;
86 | }
87 | }
88 |
89 | @layer utilities {
90 | }
91 |
92 | [data-highlighted-chars] {
93 | @apply bg-zinc-900 rounded;
94 | box-shadow: 2px 2px 0 2px rgba(139, 139, 148, 0.5);
95 | }
96 | [data-highlighted-chars] .dark {
97 | @apply bg-zinc-700/50 rounded;
98 | box-shadow: 2px 2px 0 2px rgba(139, 139, 148, 0.5);
99 | }
100 | [data-highlighted-chars] * {
101 | @apply !text-white;
102 | }
103 | [data-rehype-pretty-code-figure] pre {
104 | @apply pb-4 pt-6 max-h-[650px] overflow-x-auto rounded-lg border !bg-transparent;
105 | }
106 | [data-rehype-pretty-code-figure] [data-line] {
107 | @apply inline-block min-h-4 w-full py-0.5 px-4;
108 | }
109 |
110 | .code-example-overlay {
111 | background-image: linear-gradient(
112 | to bottom,
113 | theme("colors.background") 60%,
114 | transparent
115 | );
116 | transform: translateY(0);
117 | animation: move-overlay 4s ease-out forwards;
118 | animation-delay: 3s;
119 | }
120 | .code-example-light {
121 | }
122 | .code-example-dark {
123 | display: none;
124 | }
125 | @media (prefers-color-scheme: dark) {
126 | .code-example-light {
127 | display: none;
128 | }
129 | .code-example-dark {
130 | display: unset;
131 | }
132 | }
133 |
134 | @media (prefers-reduced-motion: reduce) {
135 | .code-example-overlay {
136 | opacity: 0;
137 | animation: none;
138 | }
139 | }
140 | @keyframes move-overlay {
141 | 0% {
142 | transform: translateY(0);
143 | }
144 | 100% {
145 | transform: translateY(-100%);
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/components/copy-button.tsx:
--------------------------------------------------------------------------------
1 | // Stolen from @shadcn/ui the man the machine!!
2 | "use client"
3 |
4 | import * as React from "react"
5 | import { CheckIcon, CopyIcon } from "@radix-ui/react-icons"
6 | import type { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu"
7 |
8 | import { cn } from "@/lib/utils"
9 | import { Button } from "./ui/button"
10 | import {
11 | DropdownMenu,
12 | DropdownMenuContent,
13 | DropdownMenuItem,
14 | DropdownMenuTrigger,
15 | } from "./ui/dropdown-menu"
16 |
17 | interface CopyButtonProps extends React.HTMLAttributes