nextjs-link-preview 1.0.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,187 +1,195 @@
1
- # Next.js Link Preview
2
-
3
- A simple, lightweight Next.js component for displaying beautiful link preview cards.
4
-
5
- ## Quick Start
6
-
7
- ```bash
8
- npm install nextjs-link-preview
9
- ```
10
-
11
- ```tsx
12
- import { LinkPreview } from "nextjs-link-preview";
13
-
14
- <LinkPreview
15
- url="https://github.com/vercel/next.js"
16
- title="Next.js"
17
- description="The React Framework for the Web"
18
- preset="github"
19
- />
20
- ```
21
-
22
- ## Features
23
-
24
- - ✅ Pure presentational component - no data fetching
25
- - Preset image support for GitHub and npm
26
- - ✅ Three size variants (small, medium, large)
27
- - ✅ Two layouts (vertical, horizontal)
28
- - ✅ TypeScript support
29
- - ✅ Fully customizable styling
30
- - ✅ Zero dependencies (only peer deps: React, Next.js)
31
-
32
- ## Usage
33
-
34
- ### Basic Usage
35
-
36
- ```tsx
37
- import { LinkPreview } from "nextjs-link-preview";
38
-
39
- export default function Page() {
40
- return (
41
- <LinkPreview
42
- url="https://example.com"
43
- title="Example Site"
44
- description="This is an example website"
45
- image="https://example.com/preview.png"
46
- />
47
- );
48
- }
49
- ```
50
-
51
- ### With Presets
52
-
53
- Use built-in presets for popular platforms:
54
-
55
- ```tsx
56
- // GitHub
57
- <LinkPreview
58
- url="https://github.com/user/repo"
59
- title="My Repository"
60
- description="A cool open source project"
61
- preset="github"
62
- />
63
-
64
- // npm
65
- <LinkPreview
66
- url="https://npmjs.com/package/my-package"
67
- title="my-package"
68
- description="An awesome npm package"
69
- preset="npm"
70
- />
71
- ```
72
-
73
- ### Size Variants
74
-
75
- ```tsx
76
- <LinkPreview url="..." title="..." image="..." size="small" />
77
- <LinkPreview url="..." title="..." image="..." size="medium" /> {/* default */}
78
- <LinkPreview url="..." title="..." image="..." size="large" />
79
- ```
80
-
81
- ### Layouts
82
-
83
- ```tsx
84
- {/* Vertical (default) - image on top */}
85
- <LinkPreview url="..." title="..." image="..." layout="vertical" />
86
-
87
- {/* Horizontal - image on left */}
88
- <LinkPreview url="..." title="..." image="..." layout="horizontal" />
89
- ```
90
-
91
- ### Custom Styling
92
-
93
- ```tsx
94
- <LinkPreview
95
- url="https://example.com"
96
- title="Example"
97
- image="..."
98
- width="400px"
99
- className="my-custom-class"
100
- />
101
- ```
102
-
103
- ## Props
104
-
105
- | Prop | Type | Default | Description |
106
- | ------------- | ------------------------------------ | ------------ | -------------------------------------- |
107
- | `url` | `string` | **required** | Link destination |
108
- | `title` | `string` | **required** | Preview card title |
109
- | `description` | `string` | `undefined` | Preview card description (optional) |
110
- | `image` | `string` | `undefined` | Custom image URL (optional) |
111
- | `preset` | `"github"` \| `"npm"` | `undefined` | Use preset image (optional) |
112
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | Preview card size |
113
- | `layout` | `"vertical"` \| `"horizontal"` | `"vertical"` | Image position |
114
- | `width` | `string` \| `number` | `"100%"` | Card width |
115
- | `height` | `string` \| `number` | `"auto"` | Card height |
116
- | `className` | `string` | `""` | Additional CSS classes |
117
-
118
- **Note:** Either `image` or `preset` should be provided. If both are provided, `image` takes precedence.
119
-
120
- ## Presets
121
-
122
- Available presets:
123
-
124
- - `github` - GitHub logo
125
- - `npm` - npm logo
126
-
127
- ## Examples
128
-
129
- ### GitHub Repository Preview
130
-
131
- ```tsx
132
- <LinkPreview
133
- url="https://github.com/vercel/next.js"
134
- title="Next.js"
135
- description="The React Framework for the Web"
136
- preset="github"
137
- size="large"
138
- />
139
- ```
140
-
141
- ### npm Package Preview
142
-
143
- ```tsx
144
- <LinkPreview
145
- url="https://npmjs.com/package/react"
146
- title="react"
147
- description="React is a JavaScript library for building user interfaces."
148
- preset="npm"
149
- layout="horizontal"
150
- />
151
- ```
152
-
153
- ### Custom Article Preview
154
-
155
- ```tsx
156
- <LinkPreview
157
- url="https://example.com/article"
158
- title="How to Build Amazing Web Apps"
159
- description="Learn the best practices for modern web development"
160
- image="https://example.com/article-cover.jpg"
161
- size="medium"
162
- />
163
- ```
164
-
165
- ## Testing
166
-
167
- To test the component locally:
168
-
169
- ```bash
170
- npm test
171
- ```
172
-
173
- This will:
174
- 1. Build the component
175
- 2. Start the demo at http://localhost:3000
176
- 3. Open your browser to see the interactive demo
177
-
178
- That's it!
179
-
180
- ## License
181
-
182
- MIT
183
-
184
- ## Links
185
-
186
- - [GitHub](https://github.com/sethcarney/nextjs-link-preview)
187
- - [npm](https://www.npmjs.com/package/nextjs-link-preview)
1
+ # Next.js Link Preview
2
+
3
+ A simple, lightweight Next.js component for displaying beautiful link preview cards.
4
+
5
+ ## Quick Start
6
+
7
+ ### Option 1: Install as npm package
8
+
9
+ ```bash
10
+ npm install nextjs-link-preview
11
+ ```
12
+
13
+ ```tsx
14
+ import { FaGithub } from "react-icons/fa";
15
+ import { LinkPreview } from "nextjs-link-preview";
16
+
17
+ <LinkPreview
18
+ url="https://github.com/vercel/next.js"
19
+ title="Next.js"
20
+ description="The React Framework for the Web"
21
+ preset={FaGithub}
22
+ />;
23
+ ```
24
+
25
+ ### Option 2: Copy to your project
26
+
27
+ ```bash
28
+ npx nextjs-link-preview init
29
+ ```
30
+
31
+ This copies the component source into `src/components/LinkPreview.tsx`. You own the code and can customize it however you want.
32
+
33
+ ```bash
34
+ # Custom path
35
+ npx nextjs-link-preview init --path src/ui/LinkPreview.tsx
36
+ ```
37
+
38
+ ## Features
39
+
40
+ - Pure presentational component - no data fetching
41
+ - Preset icon support via react-icons
42
+ - Custom icon support (react-icons, lucide, heroicons, any React element)
43
+ - Customizable text colors
44
+ - Three size variants (small, medium, large)
45
+ - Two layouts (vertical, horizontal)
46
+ - TypeScript support
47
+ - Fully customizable styling
48
+ - CLI copy-to-source support
49
+ - Peer deps only: React, Next.js, react-icons
50
+
51
+ ## Usage
52
+
53
+ ### Basic Usage
54
+
55
+ ```tsx
56
+ import { LinkPreview } from "nextjs-link-preview";
57
+
58
+ export default function Page() {
59
+ return (
60
+ <LinkPreview
61
+ url="https://example.com"
62
+ title="Example Site"
63
+ description="This is an example website"
64
+ image="https://example.com/preview.png"
65
+ />
66
+ );
67
+ }
68
+ ```
69
+
70
+ ### With Preset Icons
71
+
72
+ ```tsx
73
+ import { FaGithub, FaNpm } from "react-icons/fa";
74
+
75
+ // GitHub
76
+ <LinkPreview
77
+ url="https://github.com/user/repo"
78
+ title="My Repository"
79
+ description="A cool open source project"
80
+ preset={FaGithub}
81
+ />
82
+
83
+ // npm
84
+ <LinkPreview
85
+ url="https://npmjs.com/package/my-package"
86
+ title="my-package"
87
+ description="An awesome npm package"
88
+ preset={FaNpm}
89
+ />
90
+ ```
91
+
92
+ ### With Icons
93
+
94
+ Pass any React element as an icon. Works with react-icons, lucide, heroicons, or plain SVGs:
95
+
96
+ ```tsx
97
+ import { FaGithub } from "react-icons/fa";
98
+
99
+ <LinkPreview
100
+ url="https://github.com/user/repo"
101
+ title="My Repository"
102
+ description="A cool open source project"
103
+ icon={<FaGithub size={48} />}
104
+ />;
105
+ ```
106
+
107
+ Priority order: `icon` > `image` > `preset`.
108
+
109
+ ### Custom Text Colors
110
+
111
+ ```tsx
112
+ <LinkPreview
113
+ url="https://example.com"
114
+ title="Styled Preview"
115
+ description="With custom colors"
116
+ image="https://example.com/preview.png"
117
+ titleColor="#1a1a2e"
118
+ descriptionColor="#999"
119
+ />
120
+ ```
121
+
122
+ ### Size Variants
123
+
124
+ ```tsx
125
+ <LinkPreview url="..." title="..." image="..." size="small" />
126
+ <LinkPreview url="..." title="..." image="..." size="medium" /> {/* default */}
127
+ <LinkPreview url="..." title="..." image="..." size="large" />
128
+ ```
129
+
130
+ ### Layouts
131
+
132
+ ```tsx
133
+ {/* Vertical (default) - image on top */}
134
+ <LinkPreview url="..." title="..." image="..." layout="vertical" />
135
+
136
+ {/* Horizontal - image on left */}
137
+ <LinkPreview url="..." title="..." image="..." layout="horizontal" />
138
+ ```
139
+
140
+ ### Custom Styling
141
+
142
+ ```tsx
143
+ <LinkPreview
144
+ url="https://example.com"
145
+ title="Example"
146
+ image="..."
147
+ width="400px"
148
+ className="my-custom-class"
149
+ />
150
+ ```
151
+
152
+ ## Props
153
+
154
+ | Prop | Type | Default | Description |
155
+ | ------------------ | ------------------------------------ | ------------ | ------------------------------------------------------ |
156
+ | `url` | `string` | **required** | Link destination |
157
+ | `title` | `string` | **required** | Preview card title |
158
+ | `description` | `string` | `undefined` | Preview card description |
159
+ | `image` | `string` | `undefined` | Custom image URL |
160
+ | `preset` | `IconType` | `undefined` | Preset icon component (from react-icons) |
161
+ | `icon` | `React.ReactNode` | `undefined` | Custom icon element (takes priority over image/preset) |
162
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | Preview card size |
163
+ | `layout` | `"vertical"` \| `"horizontal"` | `"vertical"` | Image position |
164
+ | `width` | `string` \| `number` | `"100%"` | Card width |
165
+ | `height` | `string` \| `number` | `"auto"` | Card height |
166
+ | `className` | `string` | `""` | Additional CSS classes |
167
+ | `titleColor` | `string` | `undefined` | Custom title text color |
168
+ | `descriptionColor` | `string` | `"#666"` | Custom description text color |
169
+
170
+ ## Preset Icons
171
+
172
+ Pass any react-icons component as the `preset` prop. This gives you access to the full react-icons catalog.
173
+
174
+ ## Testing
175
+
176
+ To test the component locally:
177
+
178
+ ```bash
179
+ npm test
180
+ ```
181
+
182
+ This will:
183
+
184
+ 1. Build the component
185
+ 2. Start the demo at http://localhost:3000
186
+ 3. Open your browser to see the interactive demo
187
+
188
+ ## License
189
+
190
+ MIT
191
+
192
+ ## Links
193
+
194
+ - [GitHub](https://github.com/sethcarney/nextjs-link-preview)
195
+ - [npm](https://www.npmjs.com/package/nextjs-link-preview)
package/bin/cli.mjs ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
4
+ import { resolve, dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ const args = process.argv.slice(2);
11
+ const command = args[0];
12
+
13
+ if (!command || command === "--help" || command === "-h") {
14
+ console.log(`
15
+ nextjs-link-preview CLI
16
+
17
+ Usage:
18
+ npx nextjs-link-preview init [--path <destination>]
19
+
20
+ Commands:
21
+ init Copy the LinkPreview component into your project
22
+
23
+ Options:
24
+ --path Destination path (default: src/components/LinkPreview.tsx)
25
+ --help Show this help message
26
+ `);
27
+ process.exit(command === "--help" || command === "-h" ? 0 : 1);
28
+ }
29
+
30
+ if (command !== "init") {
31
+ console.error(`Unknown command: ${command}`);
32
+ console.error('Run "npx nextjs-link-preview --help" for usage info.');
33
+ process.exit(1);
34
+ }
35
+
36
+ // Parse --path flag
37
+ let destPath = "src/components/LinkPreview.tsx";
38
+ const pathIndex = args.indexOf("--path");
39
+ if (pathIndex !== -1 && args[pathIndex + 1]) {
40
+ destPath = args[pathIndex + 1];
41
+ }
42
+
43
+ const sourcePath = join(__dirname, "..", "src", "nextjs", "components", "LinkPreview.tsx");
44
+ const targetPath = resolve(process.cwd(), destPath);
45
+ const targetDir = dirname(targetPath);
46
+
47
+ if (!existsSync(sourcePath)) {
48
+ console.error("Error: Could not find LinkPreview.tsx source file.");
49
+ console.error("This may be a packaging issue. Please report it at:");
50
+ console.error("https://github.com/sethcarney/nextjs-link-preview/issues");
51
+ process.exit(1);
52
+ }
53
+
54
+ if (existsSync(targetPath)) {
55
+ console.log(`File already exists at: ${destPath}`);
56
+ console.log("Overwriting with the latest version...\n");
57
+ }
58
+
59
+ if (!existsSync(targetDir)) {
60
+ mkdirSync(targetDir, { recursive: true });
61
+ }
62
+
63
+ try {
64
+ const source = readFileSync(sourcePath, "utf-8");
65
+ writeFileSync(targetPath, source, "utf-8");
66
+
67
+ const importPath = destPath.replace(/\.tsx$/, "").replace(/\\/g, "/");
68
+ console.log(`
69
+ LinkPreview component copied to: ${destPath}
70
+
71
+ Usage:
72
+ import { LinkPreview } from './${importPath}';
73
+
74
+ <LinkPreview
75
+ url="https://github.com/user/repo"
76
+ title="My Repo"
77
+ description="A cool repository"
78
+ preset="github"
79
+ />
80
+
81
+ The component is now yours to customize!
82
+ `);
83
+ } catch (err) {
84
+ console.error("Error copying component:", err.message);
85
+ process.exit(1);
86
+ }
package/dist/index.d.ts CHANGED
@@ -1,41 +1,41 @@
1
1
  import React from 'react';
2
+ import { IconType } from 'react-icons';
2
3
 
3
4
  /**
4
5
  * Simple Link Preview Component
5
6
  *
6
7
  * Usage with custom image:
7
- * <LinkPreview
8
- * url="https://example.com"
9
- * title="Example"
10
- * description="Example description"
11
- * image="https://example.com/image.png"
12
- * />
8
+ * <LinkPreview url="..." title="..." image="https://example.com/image.png" />
13
9
  *
14
- * Usage with preset:
15
- * <LinkPreview
16
- * url="https://github.com/user/repo"
17
- * title="My Repo"
18
- * description="A cool repository"
19
- * preset="github"
20
- * />
10
+ * Usage with preset icon (react-icons component):
11
+ * <LinkPreview url="..." title="..." preset={FaGithub} />
12
+ *
13
+ * Usage with icon (any React element, e.g. react-icons):
14
+ * <LinkPreview url="..." title="..." icon={<FaGithub size={48} />} />
15
+ *
16
+ * Usage with custom text colors:
17
+ * <LinkPreview url="..." title="..." titleColor="#333" descriptionColor="#999" />
21
18
  */
22
19
 
23
20
  type LinkPreviewSize = "small" | "medium" | "large";
24
21
  type LinkPreviewLayout = "vertical" | "horizontal";
25
- type LinkPreviewPreset = "github" | "npm";
22
+ type LinkPreviewPreset = IconType;
26
23
  interface LinkPreviewProps {
27
24
  url: string;
28
25
  title: string;
29
26
  description?: string;
30
27
  image?: string;
31
28
  preset?: LinkPreviewPreset;
29
+ icon?: React.ReactNode;
32
30
  size?: LinkPreviewSize;
33
31
  layout?: LinkPreviewLayout;
34
32
  width?: string | number;
35
33
  height?: string | number;
36
34
  className?: string;
35
+ titleColor?: string;
36
+ descriptionColor?: string;
37
37
  }
38
- declare function LinkPreview({ url, title, description, image, preset, size, layout, width, height, className }: LinkPreviewProps): React.JSX.Element;
38
+ declare function LinkPreview({ url, title, description, image, preset, icon, size, layout, width, height, className, titleColor, descriptionColor }: LinkPreviewProps): React.JSX.Element;
39
39
 
40
40
  export { LinkPreview, LinkPreview as default };
41
41
  export type { LinkPreviewLayout, LinkPreviewPreset, LinkPreviewProps, LinkPreviewSize };
package/dist/index.esm.js CHANGED
@@ -1,11 +1,6 @@
1
1
  'use client';
2
2
  import React from 'react';
3
3
 
4
- // Inline SVG data URIs for preset images
5
- const PRESET_IMAGES = {
6
- github: `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjMjQyOTJmIi8+PC9zdmc+`,
7
- npm: "https://avatars.githubusercontent.com/u/6078720?s=200&v=4"
8
- };
9
4
  const sizeConfig = {
10
5
  small: {
11
6
  imageHeight: "120px",
@@ -32,11 +27,12 @@ const sizeConfig = {
32
27
  lineClamp: 3
33
28
  }
34
29
  };
35
- function LinkPreview({ url, title, description, image, preset, size = "medium", layout = "vertical", width = "100%", height = "auto", className = "" }) {
30
+ function LinkPreview({ url, title, description, image, preset, icon, size = "medium", layout = "vertical", width = "100%", height = "auto", className = "", titleColor, descriptionColor }) {
36
31
  const config = sizeConfig[size];
37
32
  const isHorizontal = layout === "horizontal";
38
- // Use preset image if no custom image provided
39
- const imageUrl = image || (preset ? PRESET_IMAGES[preset] : undefined);
33
+ const PresetIcon = preset;
34
+ const iconSize = size === "large" ? 96 : size === "medium" ? 64 : 48;
35
+ const iconFontSize = size === "large" ? "96px" : size === "medium" ? "64px" : "48px";
40
36
  return (React.createElement("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: `link-preview ${className}`, style: {
41
37
  display: isHorizontal ? "flex" : "block",
42
38
  flexDirection: isHorizontal ? "row" : undefined,
@@ -54,17 +50,36 @@ function LinkPreview({ url, title, description, image, preset, size = "medium",
54
50
  }, onMouseLeave: (e) => {
55
51
  e.currentTarget.style.boxShadow = "none";
56
52
  } },
57
- imageUrl && (React.createElement("div", { style: {
53
+ icon ? (React.createElement("div", { style: {
54
+ width: isHorizontal ? config.imageWidth : "100%",
55
+ height: isHorizontal ? "100%" : config.imageHeight,
56
+ minHeight: isHorizontal ? config.imageHeight : undefined,
57
+ flexShrink: isHorizontal ? 0 : undefined,
58
+ display: "flex",
59
+ alignItems: "center",
60
+ justifyContent: "center",
61
+ backgroundColor: "#f6f8fa",
62
+ fontSize: iconFontSize
63
+ } }, icon)) : image ? (React.createElement("div", { style: {
58
64
  width: isHorizontal ? config.imageWidth : "100%",
59
65
  height: isHorizontal ? "100%" : config.imageHeight,
60
66
  minHeight: isHorizontal ? config.imageHeight : undefined,
61
67
  flexShrink: isHorizontal ? 0 : undefined,
62
- backgroundImage: `url(${imageUrl})`,
63
- backgroundSize: preset ? "contain" : "cover",
68
+ backgroundImage: `url(${image})`,
69
+ backgroundSize: "cover",
64
70
  backgroundPosition: "center",
65
- backgroundRepeat: "no-repeat",
66
- backgroundColor: preset ? "#f6f8fa" : "transparent"
67
- } })),
71
+ backgroundRepeat: "no-repeat"
72
+ } })) : PresetIcon ? (React.createElement("div", { style: {
73
+ width: isHorizontal ? config.imageWidth : "100%",
74
+ height: isHorizontal ? "100%" : config.imageHeight,
75
+ minHeight: isHorizontal ? config.imageHeight : undefined,
76
+ flexShrink: isHorizontal ? 0 : undefined,
77
+ display: "flex",
78
+ alignItems: "center",
79
+ justifyContent: "center",
80
+ backgroundColor: "#f6f8fa"
81
+ } },
82
+ React.createElement(PresetIcon, { size: iconSize }))) : null,
68
83
  React.createElement("div", { style: {
69
84
  padding: config.padding,
70
85
  flex: isHorizontal ? 1 : undefined,
@@ -72,11 +87,11 @@ function LinkPreview({ url, title, description, image, preset, size = "medium",
72
87
  flexDirection: "column",
73
88
  justifyContent: "center"
74
89
  } },
75
- React.createElement("h3", { style: { margin: "0 0 8px 0", fontSize: config.titleSize } }, title),
90
+ React.createElement("h3", { style: { margin: "0 0 8px 0", fontSize: config.titleSize, color: titleColor } }, title),
76
91
  description && (React.createElement("p", { style: {
77
92
  margin: 0,
78
93
  fontSize: config.descriptionSize,
79
- color: "#666",
94
+ color: descriptionColor || "#666",
80
95
  display: "-webkit-box",
81
96
  WebkitLineClamp: config.lineClamp,
82
97
  WebkitBoxOrient: "vertical",
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/nextjs/components/LinkPreview.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Simple Link Preview Component\n *\n * Usage with custom image:\n * <LinkPreview\n * url=\"https://example.com\"\n * title=\"Example\"\n * description=\"Example description\"\n * image=\"https://example.com/image.png\"\n * />\n *\n * Usage with preset:\n * <LinkPreview\n * url=\"https://github.com/user/repo\"\n * title=\"My Repo\"\n * description=\"A cool repository\"\n * preset=\"github\"\n * />\n */\n\nimport React from \"react\";\n\nexport type LinkPreviewSize = \"small\" | \"medium\" | \"large\";\nexport type LinkPreviewLayout = \"vertical\" | \"horizontal\";\nexport type LinkPreviewPreset = \"github\" | \"npm\";\n\n// Inline SVG data URIs for preset images\nconst PRESET_IMAGES: Record<LinkPreviewPreset, string> = {\n github: `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjMjQyOTJmIi8+PC9zdmc+`,\n npm: \"https://avatars.githubusercontent.com/u/6078720?s=200&v=4\"\n};\n\nexport interface LinkPreviewProps {\n url: string;\n title: string;\n description?: string;\n image?: string;\n preset?: LinkPreviewPreset;\n size?: LinkPreviewSize;\n layout?: LinkPreviewLayout;\n width?: string | number;\n height?: string | number;\n className?: string;\n}\n\nconst sizeConfig = {\n small: {\n imageHeight: \"120px\",\n imageWidth: \"120px\",\n titleSize: \"14px\",\n descriptionSize: \"12px\",\n padding: \"8px\",\n lineClamp: 1\n },\n medium: {\n imageHeight: \"200px\",\n imageWidth: \"200px\",\n titleSize: \"16px\",\n descriptionSize: \"14px\",\n padding: \"12px\",\n lineClamp: 2\n },\n large: {\n imageHeight: \"300px\",\n imageWidth: \"280px\",\n titleSize: \"20px\",\n descriptionSize: \"16px\",\n padding: \"16px\",\n lineClamp: 3\n }\n};\n\nexport function LinkPreview({\n url,\n title,\n description,\n image,\n preset,\n size = \"medium\",\n layout = \"vertical\",\n width = \"100%\",\n height = \"auto\",\n className = \"\"\n}: LinkPreviewProps) {\n const config = sizeConfig[size];\n const isHorizontal = layout === \"horizontal\";\n\n // Use preset image if no custom image provided\n const imageUrl = image || (preset ? PRESET_IMAGES[preset] : undefined);\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={`link-preview ${className}`}\n style={{\n display: isHorizontal ? \"flex\" : \"block\",\n flexDirection: isHorizontal ? \"row\" : undefined,\n width,\n maxWidth: isHorizontal ? undefined : \"400px\",\n height,\n textDecoration: \"none\",\n color: \"inherit\",\n border: \"1px solid #e0e0e0\",\n borderRadius: \"8px\",\n overflow: \"hidden\",\n transition: \"box-shadow 0.3s\"\n }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.boxShadow = \"none\";\n }}\n >\n {imageUrl && (\n <div\n style={{\n width: isHorizontal ? config.imageWidth : \"100%\",\n height: isHorizontal ? \"100%\" : config.imageHeight,\n minHeight: isHorizontal ? config.imageHeight : undefined,\n flexShrink: isHorizontal ? 0 : undefined,\n backgroundImage: `url(${imageUrl})`,\n backgroundSize: preset ? \"contain\" : \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n backgroundColor: preset ? \"#f6f8fa\" : \"transparent\"\n }}\n />\n )}\n <div\n style={{\n padding: config.padding,\n flex: isHorizontal ? 1 : undefined,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\"\n }}\n >\n <h3 style={{ margin: \"0 0 8px 0\", fontSize: config.titleSize }}>{title}</h3>\n {description && (\n <p\n style={\n {\n margin: 0,\n fontSize: config.descriptionSize,\n color: \"#666\",\n display: \"-webkit-box\",\n WebkitLineClamp: config.lineClamp,\n WebkitBoxOrient: \"vertical\",\n overflow: \"hidden\"\n } as React.CSSProperties\n }\n >\n {description}\n </p>\n )}\n </div>\n </a>\n );\n}\n\nexport default LinkPreview;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/nextjs/components/LinkPreview.tsx"],"sourcesContent":["\"use client\";\r\n\r\n/**\r\n * Simple Link Preview Component\r\n *\r\n * Usage with custom image:\r\n * <LinkPreview url=\"...\" title=\"...\" image=\"https://example.com/image.png\" />\r\n *\r\n * Usage with preset icon (react-icons component):\r\n * <LinkPreview url=\"...\" title=\"...\" preset={FaGithub} />\r\n *\r\n * Usage with icon (any React element, e.g. react-icons):\r\n * <LinkPreview url=\"...\" title=\"...\" icon={<FaGithub size={48} />} />\r\n *\r\n * Usage with custom text colors:\r\n * <LinkPreview url=\"...\" title=\"...\" titleColor=\"#333\" descriptionColor=\"#999\" />\r\n */\r\n\r\nimport React from \"react\";\r\nimport type { IconType } from \"react-icons\";\r\n\r\nexport type LinkPreviewSize = \"small\" | \"medium\" | \"large\";\r\nexport type LinkPreviewLayout = \"vertical\" | \"horizontal\";\r\nexport type LinkPreviewPreset = IconType;\r\n\r\nexport interface LinkPreviewProps {\r\n url: string;\r\n title: string;\r\n description?: string;\r\n image?: string;\r\n preset?: LinkPreviewPreset;\r\n icon?: React.ReactNode;\r\n size?: LinkPreviewSize;\r\n layout?: LinkPreviewLayout;\r\n width?: string | number;\r\n height?: string | number;\r\n className?: string;\r\n titleColor?: string;\r\n descriptionColor?: string;\r\n}\r\n\r\nconst sizeConfig = {\r\n small: {\r\n imageHeight: \"120px\",\r\n imageWidth: \"120px\",\r\n titleSize: \"14px\",\r\n descriptionSize: \"12px\",\r\n padding: \"8px\",\r\n lineClamp: 1\r\n },\r\n medium: {\r\n imageHeight: \"200px\",\r\n imageWidth: \"200px\",\r\n titleSize: \"16px\",\r\n descriptionSize: \"14px\",\r\n padding: \"12px\",\r\n lineClamp: 2\r\n },\r\n large: {\r\n imageHeight: \"300px\",\r\n imageWidth: \"280px\",\r\n titleSize: \"20px\",\r\n descriptionSize: \"16px\",\r\n padding: \"16px\",\r\n lineClamp: 3\r\n }\r\n};\r\n\r\nexport function LinkPreview({\r\n url,\r\n title,\r\n description,\r\n image,\r\n preset,\r\n icon,\r\n size = \"medium\",\r\n layout = \"vertical\",\r\n width = \"100%\",\r\n height = \"auto\",\r\n className = \"\",\r\n titleColor,\r\n descriptionColor\r\n}: LinkPreviewProps) {\r\n const config = sizeConfig[size];\r\n const isHorizontal = layout === \"horizontal\";\r\n\r\n const PresetIcon = preset;\r\n const iconSize = size === \"large\" ? 96 : size === \"medium\" ? 64 : 48;\r\n const iconFontSize = size === \"large\" ? \"96px\" : size === \"medium\" ? \"64px\" : \"48px\";\r\n\r\n return (\r\n <a\r\n href={url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className={`link-preview ${className}`}\r\n style={{\r\n display: isHorizontal ? \"flex\" : \"block\",\r\n flexDirection: isHorizontal ? \"row\" : undefined,\r\n width,\r\n maxWidth: isHorizontal ? undefined : \"400px\",\r\n height,\r\n textDecoration: \"none\",\r\n color: \"inherit\",\r\n border: \"1px solid #e0e0e0\",\r\n borderRadius: \"8px\",\r\n overflow: \"hidden\",\r\n transition: \"box-shadow 0.3s\"\r\n }}\r\n onMouseEnter={(e) => {\r\n (e.currentTarget as HTMLElement).style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n (e.currentTarget as HTMLElement).style.boxShadow = \"none\";\r\n }}\r\n >\r\n {icon ? (\r\n <div\r\n style={{\r\n width: isHorizontal ? config.imageWidth : \"100%\",\r\n height: isHorizontal ? \"100%\" : config.imageHeight,\r\n minHeight: isHorizontal ? config.imageHeight : undefined,\r\n flexShrink: isHorizontal ? 0 : undefined,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n backgroundColor: \"#f6f8fa\",\r\n fontSize: iconFontSize\r\n }}\r\n >\r\n {icon}\r\n </div>\r\n ) : image ? (\r\n <div\r\n style={{\r\n width: isHorizontal ? config.imageWidth : \"100%\",\r\n height: isHorizontal ? \"100%\" : config.imageHeight,\r\n minHeight: isHorizontal ? config.imageHeight : undefined,\r\n flexShrink: isHorizontal ? 0 : undefined,\r\n backgroundImage: `url(${image})`,\r\n backgroundSize: \"cover\",\r\n backgroundPosition: \"center\",\r\n backgroundRepeat: \"no-repeat\"\r\n }}\r\n />\r\n ) : PresetIcon ? (\r\n <div\r\n style={{\r\n width: isHorizontal ? config.imageWidth : \"100%\",\r\n height: isHorizontal ? \"100%\" : config.imageHeight,\r\n minHeight: isHorizontal ? config.imageHeight : undefined,\r\n flexShrink: isHorizontal ? 0 : undefined,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n backgroundColor: \"#f6f8fa\"\r\n }}\r\n >\r\n <PresetIcon size={iconSize} />\r\n </div>\r\n ) : null}\r\n <div\r\n style={{\r\n padding: config.padding,\r\n flex: isHorizontal ? 1 : undefined,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n justifyContent: \"center\"\r\n }}\r\n >\r\n <h3 style={{ margin: \"0 0 8px 0\", fontSize: config.titleSize, color: titleColor }}>{title}</h3>\r\n {description && (\r\n <p\r\n style={\r\n {\r\n margin: 0,\r\n fontSize: config.descriptionSize,\r\n color: descriptionColor || \"#666\",\r\n display: \"-webkit-box\",\r\n WebkitLineClamp: config.lineClamp,\r\n WebkitBoxOrient: \"vertical\",\r\n overflow: \"hidden\"\r\n } as React.CSSProperties\r\n }\r\n >\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n </a>\r\n );\r\n}\r\n\r\nexport default LinkPreview;\r\n"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -5,11 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
6
  var React = require('react');
7
7
 
8
- // Inline SVG data URIs for preset images
9
- const PRESET_IMAGES = {
10
- github: `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjMjQyOTJmIi8+PC9zdmc+`,
11
- npm: "https://avatars.githubusercontent.com/u/6078720?s=200&v=4"
12
- };
13
8
  const sizeConfig = {
14
9
  small: {
15
10
  imageHeight: "120px",
@@ -36,11 +31,12 @@ const sizeConfig = {
36
31
  lineClamp: 3
37
32
  }
38
33
  };
39
- function LinkPreview({ url, title, description, image, preset, size = "medium", layout = "vertical", width = "100%", height = "auto", className = "" }) {
34
+ function LinkPreview({ url, title, description, image, preset, icon, size = "medium", layout = "vertical", width = "100%", height = "auto", className = "", titleColor, descriptionColor }) {
40
35
  const config = sizeConfig[size];
41
36
  const isHorizontal = layout === "horizontal";
42
- // Use preset image if no custom image provided
43
- const imageUrl = image || (preset ? PRESET_IMAGES[preset] : undefined);
37
+ const PresetIcon = preset;
38
+ const iconSize = size === "large" ? 96 : size === "medium" ? 64 : 48;
39
+ const iconFontSize = size === "large" ? "96px" : size === "medium" ? "64px" : "48px";
44
40
  return (React.createElement("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: `link-preview ${className}`, style: {
45
41
  display: isHorizontal ? "flex" : "block",
46
42
  flexDirection: isHorizontal ? "row" : undefined,
@@ -58,17 +54,36 @@ function LinkPreview({ url, title, description, image, preset, size = "medium",
58
54
  }, onMouseLeave: (e) => {
59
55
  e.currentTarget.style.boxShadow = "none";
60
56
  } },
61
- imageUrl && (React.createElement("div", { style: {
57
+ icon ? (React.createElement("div", { style: {
58
+ width: isHorizontal ? config.imageWidth : "100%",
59
+ height: isHorizontal ? "100%" : config.imageHeight,
60
+ minHeight: isHorizontal ? config.imageHeight : undefined,
61
+ flexShrink: isHorizontal ? 0 : undefined,
62
+ display: "flex",
63
+ alignItems: "center",
64
+ justifyContent: "center",
65
+ backgroundColor: "#f6f8fa",
66
+ fontSize: iconFontSize
67
+ } }, icon)) : image ? (React.createElement("div", { style: {
62
68
  width: isHorizontal ? config.imageWidth : "100%",
63
69
  height: isHorizontal ? "100%" : config.imageHeight,
64
70
  minHeight: isHorizontal ? config.imageHeight : undefined,
65
71
  flexShrink: isHorizontal ? 0 : undefined,
66
- backgroundImage: `url(${imageUrl})`,
67
- backgroundSize: preset ? "contain" : "cover",
72
+ backgroundImage: `url(${image})`,
73
+ backgroundSize: "cover",
68
74
  backgroundPosition: "center",
69
- backgroundRepeat: "no-repeat",
70
- backgroundColor: preset ? "#f6f8fa" : "transparent"
71
- } })),
75
+ backgroundRepeat: "no-repeat"
76
+ } })) : PresetIcon ? (React.createElement("div", { style: {
77
+ width: isHorizontal ? config.imageWidth : "100%",
78
+ height: isHorizontal ? "100%" : config.imageHeight,
79
+ minHeight: isHorizontal ? config.imageHeight : undefined,
80
+ flexShrink: isHorizontal ? 0 : undefined,
81
+ display: "flex",
82
+ alignItems: "center",
83
+ justifyContent: "center",
84
+ backgroundColor: "#f6f8fa"
85
+ } },
86
+ React.createElement(PresetIcon, { size: iconSize }))) : null,
72
87
  React.createElement("div", { style: {
73
88
  padding: config.padding,
74
89
  flex: isHorizontal ? 1 : undefined,
@@ -76,11 +91,11 @@ function LinkPreview({ url, title, description, image, preset, size = "medium",
76
91
  flexDirection: "column",
77
92
  justifyContent: "center"
78
93
  } },
79
- React.createElement("h3", { style: { margin: "0 0 8px 0", fontSize: config.titleSize } }, title),
94
+ React.createElement("h3", { style: { margin: "0 0 8px 0", fontSize: config.titleSize, color: titleColor } }, title),
80
95
  description && (React.createElement("p", { style: {
81
96
  margin: 0,
82
97
  fontSize: config.descriptionSize,
83
- color: "#666",
98
+ color: descriptionColor || "#666",
84
99
  display: "-webkit-box",
85
100
  WebkitLineClamp: config.lineClamp,
86
101
  WebkitBoxOrient: "vertical",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/nextjs/components/LinkPreview.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Simple Link Preview Component\n *\n * Usage with custom image:\n * <LinkPreview\n * url=\"https://example.com\"\n * title=\"Example\"\n * description=\"Example description\"\n * image=\"https://example.com/image.png\"\n * />\n *\n * Usage with preset:\n * <LinkPreview\n * url=\"https://github.com/user/repo\"\n * title=\"My Repo\"\n * description=\"A cool repository\"\n * preset=\"github\"\n * />\n */\n\nimport React from \"react\";\n\nexport type LinkPreviewSize = \"small\" | \"medium\" | \"large\";\nexport type LinkPreviewLayout = \"vertical\" | \"horizontal\";\nexport type LinkPreviewPreset = \"github\" | \"npm\";\n\n// Inline SVG data URIs for preset images\nconst PRESET_IMAGES: Record<LinkPreviewPreset, string> = {\n github: `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjMjQyOTJmIi8+PC9zdmc+`,\n npm: \"https://avatars.githubusercontent.com/u/6078720?s=200&v=4\"\n};\n\nexport interface LinkPreviewProps {\n url: string;\n title: string;\n description?: string;\n image?: string;\n preset?: LinkPreviewPreset;\n size?: LinkPreviewSize;\n layout?: LinkPreviewLayout;\n width?: string | number;\n height?: string | number;\n className?: string;\n}\n\nconst sizeConfig = {\n small: {\n imageHeight: \"120px\",\n imageWidth: \"120px\",\n titleSize: \"14px\",\n descriptionSize: \"12px\",\n padding: \"8px\",\n lineClamp: 1\n },\n medium: {\n imageHeight: \"200px\",\n imageWidth: \"200px\",\n titleSize: \"16px\",\n descriptionSize: \"14px\",\n padding: \"12px\",\n lineClamp: 2\n },\n large: {\n imageHeight: \"300px\",\n imageWidth: \"280px\",\n titleSize: \"20px\",\n descriptionSize: \"16px\",\n padding: \"16px\",\n lineClamp: 3\n }\n};\n\nexport function LinkPreview({\n url,\n title,\n description,\n image,\n preset,\n size = \"medium\",\n layout = \"vertical\",\n width = \"100%\",\n height = \"auto\",\n className = \"\"\n}: LinkPreviewProps) {\n const config = sizeConfig[size];\n const isHorizontal = layout === \"horizontal\";\n\n // Use preset image if no custom image provided\n const imageUrl = image || (preset ? PRESET_IMAGES[preset] : undefined);\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={`link-preview ${className}`}\n style={{\n display: isHorizontal ? \"flex\" : \"block\",\n flexDirection: isHorizontal ? \"row\" : undefined,\n width,\n maxWidth: isHorizontal ? undefined : \"400px\",\n height,\n textDecoration: \"none\",\n color: \"inherit\",\n border: \"1px solid #e0e0e0\",\n borderRadius: \"8px\",\n overflow: \"hidden\",\n transition: \"box-shadow 0.3s\"\n }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.boxShadow = \"none\";\n }}\n >\n {imageUrl && (\n <div\n style={{\n width: isHorizontal ? config.imageWidth : \"100%\",\n height: isHorizontal ? \"100%\" : config.imageHeight,\n minHeight: isHorizontal ? config.imageHeight : undefined,\n flexShrink: isHorizontal ? 0 : undefined,\n backgroundImage: `url(${imageUrl})`,\n backgroundSize: preset ? \"contain\" : \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n backgroundColor: preset ? \"#f6f8fa\" : \"transparent\"\n }}\n />\n )}\n <div\n style={{\n padding: config.padding,\n flex: isHorizontal ? 1 : undefined,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\"\n }}\n >\n <h3 style={{ margin: \"0 0 8px 0\", fontSize: config.titleSize }}>{title}</h3>\n {description && (\n <p\n style={\n {\n margin: 0,\n fontSize: config.descriptionSize,\n color: \"#666\",\n display: \"-webkit-box\",\n WebkitLineClamp: config.lineClamp,\n WebkitBoxOrient: \"vertical\",\n overflow: \"hidden\"\n } as React.CSSProperties\n }\n >\n {description}\n </p>\n )}\n </div>\n </a>\n );\n}\n\nexport default LinkPreview;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/nextjs/components/LinkPreview.tsx"],"sourcesContent":["\"use client\";\r\n\r\n/**\r\n * Simple Link Preview Component\r\n *\r\n * Usage with custom image:\r\n * <LinkPreview url=\"...\" title=\"...\" image=\"https://example.com/image.png\" />\r\n *\r\n * Usage with preset icon (react-icons component):\r\n * <LinkPreview url=\"...\" title=\"...\" preset={FaGithub} />\r\n *\r\n * Usage with icon (any React element, e.g. react-icons):\r\n * <LinkPreview url=\"...\" title=\"...\" icon={<FaGithub size={48} />} />\r\n *\r\n * Usage with custom text colors:\r\n * <LinkPreview url=\"...\" title=\"...\" titleColor=\"#333\" descriptionColor=\"#999\" />\r\n */\r\n\r\nimport React from \"react\";\r\nimport type { IconType } from \"react-icons\";\r\n\r\nexport type LinkPreviewSize = \"small\" | \"medium\" | \"large\";\r\nexport type LinkPreviewLayout = \"vertical\" | \"horizontal\";\r\nexport type LinkPreviewPreset = IconType;\r\n\r\nexport interface LinkPreviewProps {\r\n url: string;\r\n title: string;\r\n description?: string;\r\n image?: string;\r\n preset?: LinkPreviewPreset;\r\n icon?: React.ReactNode;\r\n size?: LinkPreviewSize;\r\n layout?: LinkPreviewLayout;\r\n width?: string | number;\r\n height?: string | number;\r\n className?: string;\r\n titleColor?: string;\r\n descriptionColor?: string;\r\n}\r\n\r\nconst sizeConfig = {\r\n small: {\r\n imageHeight: \"120px\",\r\n imageWidth: \"120px\",\r\n titleSize: \"14px\",\r\n descriptionSize: \"12px\",\r\n padding: \"8px\",\r\n lineClamp: 1\r\n },\r\n medium: {\r\n imageHeight: \"200px\",\r\n imageWidth: \"200px\",\r\n titleSize: \"16px\",\r\n descriptionSize: \"14px\",\r\n padding: \"12px\",\r\n lineClamp: 2\r\n },\r\n large: {\r\n imageHeight: \"300px\",\r\n imageWidth: \"280px\",\r\n titleSize: \"20px\",\r\n descriptionSize: \"16px\",\r\n padding: \"16px\",\r\n lineClamp: 3\r\n }\r\n};\r\n\r\nexport function LinkPreview({\r\n url,\r\n title,\r\n description,\r\n image,\r\n preset,\r\n icon,\r\n size = \"medium\",\r\n layout = \"vertical\",\r\n width = \"100%\",\r\n height = \"auto\",\r\n className = \"\",\r\n titleColor,\r\n descriptionColor\r\n}: LinkPreviewProps) {\r\n const config = sizeConfig[size];\r\n const isHorizontal = layout === \"horizontal\";\r\n\r\n const PresetIcon = preset;\r\n const iconSize = size === \"large\" ? 96 : size === \"medium\" ? 64 : 48;\r\n const iconFontSize = size === \"large\" ? \"96px\" : size === \"medium\" ? \"64px\" : \"48px\";\r\n\r\n return (\r\n <a\r\n href={url}\r\n target=\"_blank\"\r\n rel=\"noopener noreferrer\"\r\n className={`link-preview ${className}`}\r\n style={{\r\n display: isHorizontal ? \"flex\" : \"block\",\r\n flexDirection: isHorizontal ? \"row\" : undefined,\r\n width,\r\n maxWidth: isHorizontal ? undefined : \"400px\",\r\n height,\r\n textDecoration: \"none\",\r\n color: \"inherit\",\r\n border: \"1px solid #e0e0e0\",\r\n borderRadius: \"8px\",\r\n overflow: \"hidden\",\r\n transition: \"box-shadow 0.3s\"\r\n }}\r\n onMouseEnter={(e) => {\r\n (e.currentTarget as HTMLElement).style.boxShadow = \"0 4px 12px rgba(0,0,0,0.15)\";\r\n }}\r\n onMouseLeave={(e) => {\r\n (e.currentTarget as HTMLElement).style.boxShadow = \"none\";\r\n }}\r\n >\r\n {icon ? (\r\n <div\r\n style={{\r\n width: isHorizontal ? config.imageWidth : \"100%\",\r\n height: isHorizontal ? \"100%\" : config.imageHeight,\r\n minHeight: isHorizontal ? config.imageHeight : undefined,\r\n flexShrink: isHorizontal ? 0 : undefined,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n backgroundColor: \"#f6f8fa\",\r\n fontSize: iconFontSize\r\n }}\r\n >\r\n {icon}\r\n </div>\r\n ) : image ? (\r\n <div\r\n style={{\r\n width: isHorizontal ? config.imageWidth : \"100%\",\r\n height: isHorizontal ? \"100%\" : config.imageHeight,\r\n minHeight: isHorizontal ? config.imageHeight : undefined,\r\n flexShrink: isHorizontal ? 0 : undefined,\r\n backgroundImage: `url(${image})`,\r\n backgroundSize: \"cover\",\r\n backgroundPosition: \"center\",\r\n backgroundRepeat: \"no-repeat\"\r\n }}\r\n />\r\n ) : PresetIcon ? (\r\n <div\r\n style={{\r\n width: isHorizontal ? config.imageWidth : \"100%\",\r\n height: isHorizontal ? \"100%\" : config.imageHeight,\r\n minHeight: isHorizontal ? config.imageHeight : undefined,\r\n flexShrink: isHorizontal ? 0 : undefined,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n backgroundColor: \"#f6f8fa\"\r\n }}\r\n >\r\n <PresetIcon size={iconSize} />\r\n </div>\r\n ) : null}\r\n <div\r\n style={{\r\n padding: config.padding,\r\n flex: isHorizontal ? 1 : undefined,\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n justifyContent: \"center\"\r\n }}\r\n >\r\n <h3 style={{ margin: \"0 0 8px 0\", fontSize: config.titleSize, color: titleColor }}>{title}</h3>\r\n {description && (\r\n <p\r\n style={\r\n {\r\n margin: 0,\r\n fontSize: config.descriptionSize,\r\n color: descriptionColor || \"#666\",\r\n display: \"-webkit-box\",\r\n WebkitLineClamp: config.lineClamp,\r\n WebkitBoxOrient: \"vertical\",\r\n overflow: \"hidden\"\r\n } as React.CSSProperties\r\n }\r\n >\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n </a>\r\n );\r\n}\r\n\r\nexport default LinkPreview;\r\n"],"names":[],"mappings":"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"}
@@ -2,37 +2,37 @@
2
2
  * Simple Link Preview Component
3
3
  *
4
4
  * Usage with custom image:
5
- * <LinkPreview
6
- * url="https://example.com"
7
- * title="Example"
8
- * description="Example description"
9
- * image="https://example.com/image.png"
10
- * />
5
+ * <LinkPreview url="..." title="..." image="https://example.com/image.png" />
11
6
  *
12
- * Usage with preset:
13
- * <LinkPreview
14
- * url="https://github.com/user/repo"
15
- * title="My Repo"
16
- * description="A cool repository"
17
- * preset="github"
18
- * />
7
+ * Usage with preset icon (react-icons component):
8
+ * <LinkPreview url="..." title="..." preset={FaGithub} />
9
+ *
10
+ * Usage with icon (any React element, e.g. react-icons):
11
+ * <LinkPreview url="..." title="..." icon={<FaGithub size={48} />} />
12
+ *
13
+ * Usage with custom text colors:
14
+ * <LinkPreview url="..." title="..." titleColor="#333" descriptionColor="#999" />
19
15
  */
20
16
  import React from "react";
17
+ import type { IconType } from "react-icons";
21
18
  export type LinkPreviewSize = "small" | "medium" | "large";
22
19
  export type LinkPreviewLayout = "vertical" | "horizontal";
23
- export type LinkPreviewPreset = "github" | "npm";
20
+ export type LinkPreviewPreset = IconType;
24
21
  export interface LinkPreviewProps {
25
22
  url: string;
26
23
  title: string;
27
24
  description?: string;
28
25
  image?: string;
29
26
  preset?: LinkPreviewPreset;
27
+ icon?: React.ReactNode;
30
28
  size?: LinkPreviewSize;
31
29
  layout?: LinkPreviewLayout;
32
30
  width?: string | number;
33
31
  height?: string | number;
34
32
  className?: string;
33
+ titleColor?: string;
34
+ descriptionColor?: string;
35
35
  }
36
- export declare function LinkPreview({ url, title, description, image, preset, size, layout, width, height, className }: LinkPreviewProps): React.JSX.Element;
36
+ export declare function LinkPreview({ url, title, description, image, preset, icon, size, layout, width, height, className, titleColor, descriptionColor }: LinkPreviewProps): React.JSX.Element;
37
37
  export default LinkPreview;
38
38
  //# sourceMappingURL=LinkPreview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LinkPreview.d.ts","sourceRoot":"","sources":["../../../../src/nextjs/components/LinkPreview.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,YAAY,CAAC;AAC1D,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,KAAK,CAAC;AAQjD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6BD,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,KAAK,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,IAAe,EACf,MAAmB,EACnB,KAAc,EACd,MAAe,EACf,SAAc,EACf,EAAE,gBAAgB,qBA8ElB;AAED,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"LinkPreview.d.ts","sourceRoot":"","sources":["../../../../src/nextjs/components/LinkPreview.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,YAAY,CAAC;AAC1D,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AA6BD,wBAAgB,WAAW,CAAC,EAC1B,GAAG,EACH,KAAK,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,IAAI,EACJ,IAAe,EACf,MAAmB,EACnB,KAAc,EACd,MAAe,EACf,SAAc,EACd,UAAU,EACV,gBAAgB,EACjB,EAAE,gBAAgB,qBA6GlB;AAED,eAAe,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,72 +1,73 @@
1
- {
2
- "name": "nextjs-link-preview",
3
- "version": "1.0.6",
4
- "type": "module",
5
- "description": "A simple, lightweight Next.js component for displaying beautiful link preview cards with preset image support",
6
- "keywords": [
7
- "nextjs",
8
- "next",
9
- "react",
10
- "link-preview",
11
- "preview",
12
- "card",
13
- "component",
14
- "typescript",
15
- "presentational",
16
- "github",
17
- "npm",
18
- "preset"
19
- ],
20
- "author": "Seth Carney",
21
- "license": "MIT",
22
- "repository": {
23
- "type": "git",
24
- "url": "git+https://github.com/sethcarney/nextjs-link-preview.git"
25
- },
26
- "homepage": "https://github.com/sethcarney/nextjs-link-preview#readme",
27
- "bugs": {
28
- "url": "https://github.com/sethcarney/nextjs-link-preview/issues"
29
- },
30
- "publishConfig": {
31
- "registry": "https://registry.npmjs.org/",
32
- "access": "public"
33
- },
34
- "main": "dist/index.js",
35
- "module": "dist/index.esm.js",
36
- "types": "dist/index.d.ts",
37
- "files": [
38
- "dist",
39
- "bin",
40
- "README.md",
41
- "LICENSE"
42
- ],
43
- "bin": {
44
- "nextjs-link-preview": "bin/setup.js"
45
- },
46
- "scripts": {
47
- "build": "rollup -c",
48
- "copy-component": "cp src/nextjs/components/LinkPreview.tsx nextjs-demo/src/components/LinkPreview.tsx",
49
- "dev": "npm run copy-component && cd nextjs-demo && npm run dev",
50
- "test": "npm run copy-component && cd nextjs-demo && npm run dev",
51
- "prepublishOnly": "npm run build"
52
- },
53
- "peerDependencies": {
54
- "next": ">=14.0.0",
55
- "react": ">=18.0.0",
56
- "react-dom": ">=18.0.0"
57
- },
58
- "dependencies": {},
59
- "devDependencies": {
60
- "@rollup/plugin-commonjs": "^29.0.0",
61
- "@rollup/plugin-node-resolve": "^16.0.3",
62
- "@rollup/plugin-typescript": "^12.3.0",
63
- "@types/react": "^19.2.2",
64
- "@types/react-dom": "^19.2.2",
65
- "prettier": "^3.6.2",
66
- "rollup": "^4.53.1",
67
- "rollup-plugin-dts": "^6.2.3",
68
- "rollup-plugin-peer-deps-external": "^2.2.4",
69
- "tslib": "^2.8.1",
70
- "typescript": "^5.9.3"
71
- }
72
- }
1
+ {
2
+ "name": "nextjs-link-preview",
3
+ "version": "2.0.0",
4
+ "type": "module",
5
+ "description": "A simple, lightweight Next.js component for displaying beautiful link preview cards with preset image support",
6
+ "keywords": [
7
+ "nextjs",
8
+ "next",
9
+ "react",
10
+ "link-preview",
11
+ "preview",
12
+ "card",
13
+ "component",
14
+ "typescript",
15
+ "presentational",
16
+ "github",
17
+ "npm",
18
+ "preset"
19
+ ],
20
+ "author": "Seth Carney",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/sethcarney/nextjs-link-preview.git"
25
+ },
26
+ "homepage": "https://github.com/sethcarney/nextjs-link-preview#readme",
27
+ "bugs": {
28
+ "url": "https://github.com/sethcarney/nextjs-link-preview/issues"
29
+ },
30
+ "publishConfig": {
31
+ "registry": "https://registry.npmjs.org/",
32
+ "access": "public"
33
+ },
34
+ "main": "dist/index.js",
35
+ "module": "dist/index.esm.js",
36
+ "types": "dist/index.d.ts",
37
+ "bin": {
38
+ "nextjs-link-preview": "bin/cli.mjs"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "bin",
43
+ "src/nextjs/components/LinkPreview.tsx",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "scripts": {
48
+ "build": "rollup -c",
49
+ "dev": "node bin/cli.mjs init --path nextjs-demo/src/components/LinkPreview.tsx && cd nextjs-demo && npm run dev",
50
+ "test": "node bin/cli.mjs init --path nextjs-demo/src/components/LinkPreview.tsx && cd nextjs-demo && npm run dev",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "peerDependencies": {
54
+ "next": ">=14.0.0",
55
+ "react": ">=18.0.0",
56
+ "react-dom": ">=18.0.0",
57
+ "react-icons": ">=4.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@rollup/plugin-commonjs": "^29.0.0",
61
+ "@rollup/plugin-node-resolve": "^16.0.3",
62
+ "@rollup/plugin-typescript": "^12.3.0",
63
+ "@types/react": "^19.2.13",
64
+ "@types/react-dom": "^19.2.3",
65
+ "prettier": "^3.8.1",
66
+ "react-icons": "^5.5.0",
67
+ "rollup": "^4.57.1",
68
+ "rollup-plugin-dts": "^6.3.0",
69
+ "rollup-plugin-peer-deps-external": "^2.2.4",
70
+ "tslib": "^2.8.1",
71
+ "typescript": "^5.9.3"
72
+ }
73
+ }
@@ -0,0 +1,194 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Simple Link Preview Component
5
+ *
6
+ * Usage with custom image:
7
+ * <LinkPreview url="..." title="..." image="https://example.com/image.png" />
8
+ *
9
+ * Usage with preset icon (react-icons component):
10
+ * <LinkPreview url="..." title="..." preset={FaGithub} />
11
+ *
12
+ * Usage with icon (any React element, e.g. react-icons):
13
+ * <LinkPreview url="..." title="..." icon={<FaGithub size={48} />} />
14
+ *
15
+ * Usage with custom text colors:
16
+ * <LinkPreview url="..." title="..." titleColor="#333" descriptionColor="#999" />
17
+ */
18
+
19
+ import React from "react";
20
+ import type { IconType } from "react-icons";
21
+
22
+ export type LinkPreviewSize = "small" | "medium" | "large";
23
+ export type LinkPreviewLayout = "vertical" | "horizontal";
24
+ export type LinkPreviewPreset = IconType;
25
+
26
+ export interface LinkPreviewProps {
27
+ url: string;
28
+ title: string;
29
+ description?: string;
30
+ image?: string;
31
+ preset?: LinkPreviewPreset;
32
+ icon?: React.ReactNode;
33
+ size?: LinkPreviewSize;
34
+ layout?: LinkPreviewLayout;
35
+ width?: string | number;
36
+ height?: string | number;
37
+ className?: string;
38
+ titleColor?: string;
39
+ descriptionColor?: string;
40
+ }
41
+
42
+ const sizeConfig = {
43
+ small: {
44
+ imageHeight: "120px",
45
+ imageWidth: "120px",
46
+ titleSize: "14px",
47
+ descriptionSize: "12px",
48
+ padding: "8px",
49
+ lineClamp: 1
50
+ },
51
+ medium: {
52
+ imageHeight: "200px",
53
+ imageWidth: "200px",
54
+ titleSize: "16px",
55
+ descriptionSize: "14px",
56
+ padding: "12px",
57
+ lineClamp: 2
58
+ },
59
+ large: {
60
+ imageHeight: "300px",
61
+ imageWidth: "280px",
62
+ titleSize: "20px",
63
+ descriptionSize: "16px",
64
+ padding: "16px",
65
+ lineClamp: 3
66
+ }
67
+ };
68
+
69
+ export function LinkPreview({
70
+ url,
71
+ title,
72
+ description,
73
+ image,
74
+ preset,
75
+ icon,
76
+ size = "medium",
77
+ layout = "vertical",
78
+ width = "100%",
79
+ height = "auto",
80
+ className = "",
81
+ titleColor,
82
+ descriptionColor
83
+ }: LinkPreviewProps) {
84
+ const config = sizeConfig[size];
85
+ const isHorizontal = layout === "horizontal";
86
+
87
+ const PresetIcon = preset;
88
+ const iconSize = size === "large" ? 96 : size === "medium" ? 64 : 48;
89
+ const iconFontSize = size === "large" ? "96px" : size === "medium" ? "64px" : "48px";
90
+
91
+ return (
92
+ <a
93
+ href={url}
94
+ target="_blank"
95
+ rel="noopener noreferrer"
96
+ className={`link-preview ${className}`}
97
+ style={{
98
+ display: isHorizontal ? "flex" : "block",
99
+ flexDirection: isHorizontal ? "row" : undefined,
100
+ width,
101
+ maxWidth: isHorizontal ? undefined : "400px",
102
+ height,
103
+ textDecoration: "none",
104
+ color: "inherit",
105
+ border: "1px solid #e0e0e0",
106
+ borderRadius: "8px",
107
+ overflow: "hidden",
108
+ transition: "box-shadow 0.3s"
109
+ }}
110
+ onMouseEnter={(e) => {
111
+ (e.currentTarget as HTMLElement).style.boxShadow = "0 4px 12px rgba(0,0,0,0.15)";
112
+ }}
113
+ onMouseLeave={(e) => {
114
+ (e.currentTarget as HTMLElement).style.boxShadow = "none";
115
+ }}
116
+ >
117
+ {icon ? (
118
+ <div
119
+ style={{
120
+ width: isHorizontal ? config.imageWidth : "100%",
121
+ height: isHorizontal ? "100%" : config.imageHeight,
122
+ minHeight: isHorizontal ? config.imageHeight : undefined,
123
+ flexShrink: isHorizontal ? 0 : undefined,
124
+ display: "flex",
125
+ alignItems: "center",
126
+ justifyContent: "center",
127
+ backgroundColor: "#f6f8fa",
128
+ fontSize: iconFontSize
129
+ }}
130
+ >
131
+ {icon}
132
+ </div>
133
+ ) : image ? (
134
+ <div
135
+ style={{
136
+ width: isHorizontal ? config.imageWidth : "100%",
137
+ height: isHorizontal ? "100%" : config.imageHeight,
138
+ minHeight: isHorizontal ? config.imageHeight : undefined,
139
+ flexShrink: isHorizontal ? 0 : undefined,
140
+ backgroundImage: `url(${image})`,
141
+ backgroundSize: "cover",
142
+ backgroundPosition: "center",
143
+ backgroundRepeat: "no-repeat"
144
+ }}
145
+ />
146
+ ) : PresetIcon ? (
147
+ <div
148
+ style={{
149
+ width: isHorizontal ? config.imageWidth : "100%",
150
+ height: isHorizontal ? "100%" : config.imageHeight,
151
+ minHeight: isHorizontal ? config.imageHeight : undefined,
152
+ flexShrink: isHorizontal ? 0 : undefined,
153
+ display: "flex",
154
+ alignItems: "center",
155
+ justifyContent: "center",
156
+ backgroundColor: "#f6f8fa"
157
+ }}
158
+ >
159
+ <PresetIcon size={iconSize} />
160
+ </div>
161
+ ) : null}
162
+ <div
163
+ style={{
164
+ padding: config.padding,
165
+ flex: isHorizontal ? 1 : undefined,
166
+ display: "flex",
167
+ flexDirection: "column",
168
+ justifyContent: "center"
169
+ }}
170
+ >
171
+ <h3 style={{ margin: "0 0 8px 0", fontSize: config.titleSize, color: titleColor }}>{title}</h3>
172
+ {description && (
173
+ <p
174
+ style={
175
+ {
176
+ margin: 0,
177
+ fontSize: config.descriptionSize,
178
+ color: descriptionColor || "#666",
179
+ display: "-webkit-box",
180
+ WebkitLineClamp: config.lineClamp,
181
+ WebkitBoxOrient: "vertical",
182
+ overflow: "hidden"
183
+ } as React.CSSProperties
184
+ }
185
+ >
186
+ {description}
187
+ </p>
188
+ )}
189
+ </div>
190
+ </a>
191
+ );
192
+ }
193
+
194
+ export default LinkPreview;