x-post-card 0.1.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 ADDED
@@ -0,0 +1,125 @@
1
+ # x-post-card
2
+
3
+ Embed beautifully styled X (Twitter) post cards in your React app.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install x-post-card
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { XCard } from 'x-post-card'
15
+
16
+ function MyComponent() {
17
+ return (
18
+ <XCard
19
+ url="https://x.com/elonmusk/status/123456789"
20
+ theme="dark"
21
+ shadow="floating"
22
+ width={450}
23
+ radius={20}
24
+ />
25
+ )
26
+ }
27
+ ```
28
+
29
+ ## Props
30
+
31
+ | Prop | Type | Default | Description |
32
+ |------|------|---------|-------------|
33
+ | `url` | `string` | (required) | The X post URL to display |
34
+ | `theme` | `'light' \| 'dim' \| 'dark'` | `'light'` | Color theme |
35
+ | `shadow` | `'flat' \| 'raised' \| 'floating' \| 'elevated'` | `'floating'` | Shadow intensity |
36
+ | `width` | `number` | `450` | Card width in pixels (350-700) |
37
+ | `radius` | `number` | `20` | Border radius in pixels (0-24) |
38
+ | `apiUrl` | `string` | (hosted API) | Custom API endpoint |
39
+ | `className` | `string` | - | Custom className for wrapper |
40
+ | `fallback` | `ReactNode` | Loading skeleton | Custom loading placeholder |
41
+ | `onError` | `(error: Error) => void` | - | Error callback |
42
+ | `onLoad` | `(post: PostData) => void` | - | Load success callback |
43
+
44
+ ## Themes
45
+
46
+ ### Light
47
+ The default theme with a clean white background.
48
+
49
+ ### Dim
50
+ A muted dark theme inspired by X's dim mode.
51
+
52
+ ### Dark
53
+ A true dark theme with deep blacks.
54
+
55
+ ## Shadow Levels
56
+
57
+ - **flat**: No shadow
58
+ - **raised**: Subtle shadow for slight elevation
59
+ - **floating**: Medium shadow (default)
60
+ - **elevated**: Prominent shadow for maximum depth
61
+
62
+ ## Advanced Usage
63
+
64
+ ### Custom API Endpoint
65
+
66
+ If you're self-hosting the API, you can point to your own endpoint:
67
+
68
+ ```tsx
69
+ <XCard
70
+ url="https://x.com/user/status/123"
71
+ apiUrl="https://your-api.com/api/scrape-post"
72
+ />
73
+ ```
74
+
75
+ ### Custom Loading State
76
+
77
+ ```tsx
78
+ <XCard
79
+ url="https://x.com/user/status/123"
80
+ fallback={<div>Loading...</div>}
81
+ />
82
+ ```
83
+
84
+ ### Error Handling
85
+
86
+ ```tsx
87
+ <XCard
88
+ url="https://x.com/user/status/123"
89
+ onError={(error) => console.error('Failed to load:', error)}
90
+ />
91
+ ```
92
+
93
+ ### Using PostCard Directly
94
+
95
+ For advanced use cases, you can render the card directly with your own data:
96
+
97
+ ```tsx
98
+ import { PostCard, getThemeStyles } from 'x-post-card'
99
+
100
+ const post = {
101
+ author: {
102
+ name: 'John Doe',
103
+ handle: '@johndoe',
104
+ avatar: 'https://...',
105
+ verified: true,
106
+ },
107
+ content: {
108
+ text: 'Hello world!',
109
+ images: [],
110
+ },
111
+ timestamp: new Date().toISOString(),
112
+ }
113
+
114
+ <PostCard
115
+ post={post}
116
+ theme={getThemeStyles('dark')}
117
+ shadow="floating"
118
+ width={450}
119
+ radius={20}
120
+ />
121
+ ```
122
+
123
+ ## License
124
+
125
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,463 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/XCard.tsx
7
+
8
+ // src/themes.ts
9
+ var themeConfig = {
10
+ light: {
11
+ bg: "#FFFFFF",
12
+ textPrimary: "#000000",
13
+ textSecondary: "#666666",
14
+ border: "#E6E6E6",
15
+ accent: "#1D9BF0",
16
+ imageInnerStroke: "rgba(0, 0, 0, 0.08)",
17
+ shadowShallow: "0 1px 3px rgba(0, 0, 0, 0.06)",
18
+ shadowMedium: "0 2px 8px rgba(0, 0, 0, 0.08)",
19
+ shadowDeep: "0 4px 16px rgba(0, 0, 0, 0.12)"
20
+ },
21
+ dim: {
22
+ bg: "#15202B",
23
+ textPrimary: "#F2F2F2",
24
+ textSecondary: "#8899A6",
25
+ border: "#38444D",
26
+ accent: "#1D9BF0",
27
+ imageInnerStroke: "rgba(255, 255, 255, 0.08)",
28
+ shadowShallow: "0 1px 3px rgba(0, 0, 0, 0.2)",
29
+ shadowMedium: "0 2px 8px rgba(0, 0, 0, 0.25)",
30
+ shadowDeep: "0 4px 16px rgba(0, 0, 0, 0.3)"
31
+ },
32
+ dark: {
33
+ bg: "#000000",
34
+ textPrimary: "#FFFFFF",
35
+ textSecondary: "#71767B",
36
+ border: "#2F3336",
37
+ accent: "#1D9BF0",
38
+ imageInnerStroke: "rgba(255, 255, 255, 0.08)",
39
+ shadowShallow: "0 1px 3px rgba(0, 0, 0, 0.3)",
40
+ shadowMedium: "0 2px 8px rgba(0, 0, 0, 0.35)",
41
+ shadowDeep: "0 4px 16px rgba(0, 0, 0, 0.4)"
42
+ }
43
+ };
44
+ function getThemeStyles(theme) {
45
+ return themeConfig[theme];
46
+ }
47
+ function getShadowForIntensity(theme, intensity) {
48
+ switch (intensity) {
49
+ case "flat":
50
+ return "none";
51
+ case "raised":
52
+ return theme.shadowShallow;
53
+ case "floating":
54
+ return theme.shadowMedium;
55
+ case "elevated":
56
+ return theme.shadowDeep;
57
+ default:
58
+ return theme.shadowMedium;
59
+ }
60
+ }
61
+ function VerifiedBadge({ color }) {
62
+ return /* @__PURE__ */ jsxRuntime.jsx(
63
+ "svg",
64
+ {
65
+ width: 16,
66
+ height: 16,
67
+ viewBox: "0 0 24 24",
68
+ fill: color,
69
+ "aria-hidden": "true",
70
+ style: { flexShrink: 0 },
71
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.33 2.19c-1.4-.46-2.91-.2-3.92.81s-1.26 2.52-.8 3.91c-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91s2.52 1.26 3.91.81c.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.71 4.2L6.8 12.46l1.41-1.42 2.26 2.26 4.8-5.23 1.47 1.36-6.2 6.77z" })
72
+ }
73
+ );
74
+ }
75
+ function PostCardSkeleton({ width, radius }) {
76
+ return /* @__PURE__ */ jsxRuntime.jsxs(
77
+ "div",
78
+ {
79
+ style: {
80
+ width,
81
+ padding: 24,
82
+ borderRadius: radius,
83
+ backgroundColor: "#f3f4f6",
84
+ animation: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite"
85
+ },
86
+ children: [
87
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, marginBottom: 16 }, children: [
88
+ /* @__PURE__ */ jsxRuntime.jsx(
89
+ "div",
90
+ {
91
+ style: {
92
+ width: 48,
93
+ height: 48,
94
+ borderRadius: "50%",
95
+ backgroundColor: "#e5e7eb"
96
+ }
97
+ }
98
+ ),
99
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [
100
+ /* @__PURE__ */ jsxRuntime.jsx(
101
+ "div",
102
+ {
103
+ style: {
104
+ width: "60%",
105
+ height: 16,
106
+ borderRadius: 4,
107
+ backgroundColor: "#e5e7eb",
108
+ marginBottom: 8
109
+ }
110
+ }
111
+ ),
112
+ /* @__PURE__ */ jsxRuntime.jsx(
113
+ "div",
114
+ {
115
+ style: {
116
+ width: "40%",
117
+ height: 14,
118
+ borderRadius: 4,
119
+ backgroundColor: "#e5e7eb"
120
+ }
121
+ }
122
+ )
123
+ ] })
124
+ ] }),
125
+ /* @__PURE__ */ jsxRuntime.jsx(
126
+ "div",
127
+ {
128
+ style: {
129
+ width: "100%",
130
+ height: 60,
131
+ borderRadius: 4,
132
+ backgroundColor: "#e5e7eb"
133
+ }
134
+ }
135
+ )
136
+ ]
137
+ }
138
+ );
139
+ }
140
+ function PostCard({ post, theme, shadow, width, radius }) {
141
+ const hasImages = post.content.images.length > 0;
142
+ const CARD_PADDING = 24;
143
+ const nestedRadius = hasImages ? Math.max(0, radius - CARD_PADDING) : 16;
144
+ const boxShadow = getShadowForIntensity(theme, shadow);
145
+ return /* @__PURE__ */ jsxRuntime.jsxs(
146
+ "div",
147
+ {
148
+ style: {
149
+ width,
150
+ padding: CARD_PADDING,
151
+ backgroundColor: theme.bg,
152
+ borderWidth: 1,
153
+ borderStyle: "solid",
154
+ borderColor: theme.border,
155
+ borderRadius: radius,
156
+ boxShadow,
157
+ boxSizing: "border-box",
158
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
159
+ },
160
+ children: [
161
+ /* @__PURE__ */ jsxRuntime.jsxs(
162
+ "div",
163
+ {
164
+ style: {
165
+ display: "flex",
166
+ alignItems: "center",
167
+ gap: 12,
168
+ marginBottom: 16
169
+ },
170
+ children: [
171
+ /* @__PURE__ */ jsxRuntime.jsxs(
172
+ "div",
173
+ {
174
+ style: {
175
+ position: "relative",
176
+ width: 48,
177
+ height: 48,
178
+ borderRadius: "50%",
179
+ overflow: "hidden",
180
+ flexShrink: 0
181
+ },
182
+ children: [
183
+ post.author.avatar ? /* @__PURE__ */ jsxRuntime.jsx(
184
+ "img",
185
+ {
186
+ src: post.author.avatar,
187
+ alt: post.author.name,
188
+ style: {
189
+ width: "100%",
190
+ height: "100%",
191
+ objectFit: "cover"
192
+ },
193
+ crossOrigin: "anonymous",
194
+ referrerPolicy: "no-referrer"
195
+ }
196
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
197
+ "div",
198
+ {
199
+ style: {
200
+ width: "100%",
201
+ height: "100%",
202
+ display: "flex",
203
+ alignItems: "center",
204
+ justifyContent: "center",
205
+ backgroundColor: theme.border,
206
+ color: theme.textSecondary,
207
+ fontSize: 20,
208
+ fontWeight: 700
209
+ },
210
+ children: post.author.name.charAt(0)
211
+ }
212
+ ),
213
+ /* @__PURE__ */ jsxRuntime.jsx(
214
+ "div",
215
+ {
216
+ style: {
217
+ position: "absolute",
218
+ inset: 0,
219
+ borderRadius: "50%",
220
+ boxShadow: `inset 0 0 0 1px ${theme.imageInnerStroke}`,
221
+ pointerEvents: "none"
222
+ }
223
+ }
224
+ )
225
+ ]
226
+ }
227
+ ),
228
+ /* @__PURE__ */ jsxRuntime.jsxs(
229
+ "div",
230
+ {
231
+ style: {
232
+ flex: 1,
233
+ minWidth: 0,
234
+ display: "flex",
235
+ flexDirection: "column",
236
+ justifyContent: "center",
237
+ gap: 2
238
+ },
239
+ children: [
240
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 2 }, children: [
241
+ /* @__PURE__ */ jsxRuntime.jsx(
242
+ "span",
243
+ {
244
+ style: {
245
+ fontWeight: 700,
246
+ fontSize: 16,
247
+ color: theme.textPrimary,
248
+ overflow: "hidden",
249
+ textOverflow: "ellipsis",
250
+ whiteSpace: "nowrap"
251
+ },
252
+ children: post.author.name
253
+ }
254
+ ),
255
+ post.author.verified && /* @__PURE__ */ jsxRuntime.jsx(VerifiedBadge, { color: theme.accent })
256
+ ] }),
257
+ /* @__PURE__ */ jsxRuntime.jsx(
258
+ "span",
259
+ {
260
+ style: {
261
+ fontSize: 14,
262
+ color: theme.textSecondary
263
+ },
264
+ children: post.author.handle
265
+ }
266
+ )
267
+ ]
268
+ }
269
+ )
270
+ ]
271
+ }
272
+ ),
273
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: hasImages ? 16 : 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
274
+ "p",
275
+ {
276
+ style: {
277
+ margin: 0,
278
+ fontSize: 16,
279
+ lineHeight: 1.5,
280
+ color: theme.textPrimary,
281
+ whiteSpace: "pre-wrap",
282
+ wordBreak: "break-word"
283
+ },
284
+ children: post.content.text
285
+ }
286
+ ) }),
287
+ hasImages && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginLeft: -24, marginRight: -24, paddingLeft: 24, paddingRight: 24 }, children: /* @__PURE__ */ jsxRuntime.jsx(
288
+ "div",
289
+ {
290
+ style: {
291
+ display: "grid",
292
+ gap: 8,
293
+ gridTemplateColumns: post.content.images.length === 1 ? "1fr" : "1fr 1fr"
294
+ },
295
+ children: post.content.images.map((image, index) => {
296
+ const count = post.content.images.length;
297
+ const isThirdImage = count === 3 && index === 2;
298
+ return /* @__PURE__ */ jsxRuntime.jsxs(
299
+ "div",
300
+ {
301
+ style: {
302
+ position: "relative",
303
+ width: "100%",
304
+ overflow: "hidden",
305
+ borderRadius: nestedRadius,
306
+ aspectRatio: count === 1 ? "16/9" : isThirdImage ? "16/9" : "1",
307
+ gridColumn: isThirdImage ? "span 2" : void 0
308
+ },
309
+ children: [
310
+ /* @__PURE__ */ jsxRuntime.jsx(
311
+ "img",
312
+ {
313
+ src: image,
314
+ alt: `Post image ${index + 1}`,
315
+ style: {
316
+ position: "absolute",
317
+ inset: 0,
318
+ width: "100%",
319
+ height: "100%",
320
+ objectFit: "cover",
321
+ objectPosition: "center"
322
+ },
323
+ crossOrigin: "anonymous",
324
+ referrerPolicy: "no-referrer"
325
+ }
326
+ ),
327
+ /* @__PURE__ */ jsxRuntime.jsx(
328
+ "div",
329
+ {
330
+ style: {
331
+ position: "absolute",
332
+ inset: 0,
333
+ borderRadius: nestedRadius,
334
+ boxShadow: `inset 0 0 0 1px ${theme.imageInnerStroke}`,
335
+ pointerEvents: "none"
336
+ }
337
+ }
338
+ )
339
+ ]
340
+ },
341
+ index
342
+ );
343
+ })
344
+ }
345
+ ) })
346
+ ]
347
+ }
348
+ );
349
+ }
350
+ var DEFAULT_API_URL = "https://x-post-card.vercel.app/api/scrape-post";
351
+ function clamp(value, min, max) {
352
+ return Math.min(max, Math.max(min, value));
353
+ }
354
+ function ErrorDisplay({
355
+ error,
356
+ width,
357
+ radius
358
+ }) {
359
+ return /* @__PURE__ */ jsxRuntime.jsxs(
360
+ "div",
361
+ {
362
+ style: {
363
+ width,
364
+ padding: 24,
365
+ borderRadius: radius,
366
+ backgroundColor: "#fef2f2",
367
+ border: "1px solid #fecaca",
368
+ color: "#dc2626",
369
+ fontSize: 14,
370
+ textAlign: "center",
371
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
372
+ },
373
+ children: [
374
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontWeight: 500 }, children: "Failed to load post" }),
375
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: "8px 0 0", opacity: 0.8, fontSize: 13 }, children: error.message })
376
+ ]
377
+ }
378
+ );
379
+ }
380
+ function XCard({
381
+ url,
382
+ theme = "light",
383
+ shadow = "floating",
384
+ width = 450,
385
+ radius = 20,
386
+ apiUrl = DEFAULT_API_URL,
387
+ className,
388
+ fallback,
389
+ onError,
390
+ onLoad
391
+ }) {
392
+ const [post, setPost] = react.useState(null);
393
+ const [error, setError] = react.useState(null);
394
+ const [loading, setLoading] = react.useState(true);
395
+ const clampedWidth = clamp(width, 350, 700);
396
+ const clampedRadius = clamp(radius, 0, 24);
397
+ const themeStyles = getThemeStyles(theme);
398
+ react.useEffect(() => {
399
+ let cancelled = false;
400
+ async function fetchPost() {
401
+ setLoading(true);
402
+ setError(null);
403
+ try {
404
+ const response = await fetch(apiUrl, {
405
+ method: "POST",
406
+ headers: {
407
+ "Content-Type": "application/json"
408
+ },
409
+ body: JSON.stringify({ url })
410
+ });
411
+ if (!response.ok) {
412
+ const errorData = await response.json().catch(() => ({}));
413
+ throw new Error(errorData.error || `Failed to fetch post (${response.status})`);
414
+ }
415
+ const data = await response.json();
416
+ if (cancelled) return;
417
+ setPost(data);
418
+ onLoad?.(data);
419
+ } catch (err) {
420
+ if (cancelled) return;
421
+ const error2 = err instanceof Error ? err : new Error("Unknown error occurred");
422
+ setError(error2);
423
+ onError?.(error2);
424
+ } finally {
425
+ if (!cancelled) {
426
+ setLoading(false);
427
+ }
428
+ }
429
+ }
430
+ fetchPost();
431
+ return () => {
432
+ cancelled = true;
433
+ };
434
+ }, [url, apiUrl, onError, onLoad]);
435
+ if (loading) {
436
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: fallback || /* @__PURE__ */ jsxRuntime.jsx(PostCardSkeleton, { width: clampedWidth, radius: clampedRadius }) });
437
+ }
438
+ if (error) {
439
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(ErrorDisplay, { error, width: clampedWidth, radius: clampedRadius }) });
440
+ }
441
+ if (post) {
442
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(
443
+ PostCard,
444
+ {
445
+ post,
446
+ theme: themeStyles,
447
+ shadow,
448
+ width: clampedWidth,
449
+ radius: clampedRadius
450
+ }
451
+ ) });
452
+ }
453
+ return null;
454
+ }
455
+
456
+ exports.PostCard = PostCard;
457
+ exports.PostCardSkeleton = PostCardSkeleton;
458
+ exports.XCard = XCard;
459
+ exports.getShadowForIntensity = getShadowForIntensity;
460
+ exports.getThemeStyles = getThemeStyles;
461
+ exports.themeConfig = themeConfig;
462
+ //# sourceMappingURL=index.cjs.map
463
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/themes.ts","../src/PostCard.tsx","../src/XCard.tsx"],"names":["jsx","jsxs","useState","useEffect","error"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Theme, ThemeStyles, ShadowIntensity } from './types'\n\n/** Theme color configurations */\nexport const themeConfig: Record<Theme, ThemeStyles> = {\n light: {\n bg: '#FFFFFF',\n textPrimary: '#000000',\n textSecondary: '#666666',\n border: '#E6E6E6',\n accent: '#1D9BF0',\n imageInnerStroke: 'rgba(0, 0, 0, 0.08)',\n shadowShallow: '0 1px 3px rgba(0, 0, 0, 0.06)',\n shadowMedium: '0 2px 8px rgba(0, 0, 0, 0.08)',\n shadowDeep: '0 4px 16px rgba(0, 0, 0, 0.12)',\n },\n dim: {\n bg: '#15202B',\n textPrimary: '#F2F2F2',\n textSecondary: '#8899A6',\n border: '#38444D',\n accent: '#1D9BF0',\n imageInnerStroke: 'rgba(255, 255, 255, 0.08)',\n shadowShallow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n shadowMedium: '0 2px 8px rgba(0, 0, 0, 0.25)',\n shadowDeep: '0 4px 16px rgba(0, 0, 0, 0.3)',\n },\n dark: {\n bg: '#000000',\n textPrimary: '#FFFFFF',\n textSecondary: '#71767B',\n border: '#2F3336',\n accent: '#1D9BF0',\n imageInnerStroke: 'rgba(255, 255, 255, 0.08)',\n shadowShallow: '0 1px 3px rgba(0, 0, 0, 0.3)',\n shadowMedium: '0 2px 8px rgba(0, 0, 0, 0.35)',\n shadowDeep: '0 4px 16px rgba(0, 0, 0, 0.4)',\n },\n}\n\n/** Get theme styles for a given theme */\nexport function getThemeStyles(theme: Theme): ThemeStyles {\n return themeConfig[theme]\n}\n\n/** Get the appropriate shadow value for an intensity level */\nexport function getShadowForIntensity(\n theme: ThemeStyles,\n intensity: ShadowIntensity\n): string {\n switch (intensity) {\n case 'flat':\n return 'none'\n case 'raised':\n return theme.shadowShallow\n case 'floating':\n return theme.shadowMedium\n case 'elevated':\n return theme.shadowDeep\n default:\n return theme.shadowMedium\n }\n}\n","import * as React from 'react'\nimport type { PostData, ShadowIntensity, ThemeStyles } from './types'\nimport { getShadowForIntensity } from './themes'\n\ninterface PostCardProps {\n post: PostData\n theme: ThemeStyles\n shadow: ShadowIntensity\n width: number\n radius: number\n}\n\n/** Verified badge SVG icon */\nfunction VerifiedBadge({ color }: { color: string }) {\n return (\n <svg\n width={16}\n height={16}\n viewBox=\"0 0 24 24\"\n fill={color}\n aria-hidden=\"true\"\n style={{ flexShrink: 0 }}\n >\n <path d=\"M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.33 2.19c-1.4-.46-2.91-.2-3.92.81s-1.26 2.52-.8 3.91c-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91s2.52 1.26 3.91.81c.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.71 4.2L6.8 12.46l1.41-1.42 2.26 2.26 4.8-5.23 1.47 1.36-6.2 6.77z\" />\n </svg>\n )\n}\n\n/** Default loading skeleton */\nexport function PostCardSkeleton({ width, radius }: { width: number; radius: number }) {\n return (\n <div\n style={{\n width,\n padding: 24,\n borderRadius: radius,\n backgroundColor: '#f3f4f6',\n animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>\n <div\n style={{\n width: 48,\n height: 48,\n borderRadius: '50%',\n backgroundColor: '#e5e7eb',\n }}\n />\n <div style={{ flex: 1 }}>\n <div\n style={{\n width: '60%',\n height: 16,\n borderRadius: 4,\n backgroundColor: '#e5e7eb',\n marginBottom: 8,\n }}\n />\n <div\n style={{\n width: '40%',\n height: 14,\n borderRadius: 4,\n backgroundColor: '#e5e7eb',\n }}\n />\n </div>\n </div>\n <div\n style={{\n width: '100%',\n height: 60,\n borderRadius: 4,\n backgroundColor: '#e5e7eb',\n }}\n />\n </div>\n )\n}\n\n/** The styled post card component */\nexport function PostCard({ post, theme, shadow, width, radius }: PostCardProps) {\n const hasImages = post.content.images.length > 0\n\n // Nested radii for images (concentric with card corners)\n const CARD_PADDING = 24\n const nestedRadius = hasImages ? Math.max(0, radius - CARD_PADDING) : 16\n\n const boxShadow = getShadowForIntensity(theme, shadow)\n\n return (\n <div\n style={{\n width,\n padding: CARD_PADDING,\n backgroundColor: theme.bg,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: theme.border,\n borderRadius: radius,\n boxShadow,\n boxSizing: 'border-box',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif',\n }}\n >\n {/* Author Section */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n marginBottom: 16,\n }}\n >\n {/* Avatar */}\n <div\n style={{\n position: 'relative',\n width: 48,\n height: 48,\n borderRadius: '50%',\n overflow: 'hidden',\n flexShrink: 0,\n }}\n >\n {post.author.avatar ? (\n <img\n src={post.author.avatar}\n alt={post.author.name}\n style={{\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n }}\n crossOrigin=\"anonymous\"\n referrerPolicy=\"no-referrer\"\n />\n ) : (\n <div\n style={{\n width: '100%',\n height: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme.border,\n color: theme.textSecondary,\n fontSize: 20,\n fontWeight: 700,\n }}\n >\n {post.author.name.charAt(0)}\n </div>\n )}\n {/* Inner stroke overlay */}\n <div\n style={{\n position: 'absolute',\n inset: 0,\n borderRadius: '50%',\n boxShadow: `inset 0 0 0 1px ${theme.imageInnerStroke}`,\n pointerEvents: 'none',\n }}\n />\n </div>\n\n {/* Name and handle */}\n <div\n style={{\n flex: 1,\n minWidth: 0,\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n gap: 2,\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>\n <span\n style={{\n fontWeight: 700,\n fontSize: 16,\n color: theme.textPrimary,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {post.author.name}\n </span>\n {post.author.verified && <VerifiedBadge color={theme.accent} />}\n </div>\n <span\n style={{\n fontSize: 14,\n color: theme.textSecondary,\n }}\n >\n {post.author.handle}\n </span>\n </div>\n </div>\n\n {/* Content */}\n <div style={{ marginBottom: hasImages ? 16 : 0 }}>\n <p\n style={{\n margin: 0,\n fontSize: 16,\n lineHeight: 1.5,\n color: theme.textPrimary,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n }}\n >\n {post.content.text}\n </p>\n </div>\n\n {/* Images */}\n {hasImages && (\n <div style={{ marginLeft: -24, marginRight: -24, paddingLeft: 24, paddingRight: 24 }}>\n <div\n style={{\n display: 'grid',\n gap: 8,\n gridTemplateColumns: post.content.images.length === 1 ? '1fr' : '1fr 1fr',\n }}\n >\n {post.content.images.map((image, index) => {\n const count = post.content.images.length\n const isThirdImage = count === 3 && index === 2\n\n return (\n <div\n key={index}\n style={{\n position: 'relative',\n width: '100%',\n overflow: 'hidden',\n borderRadius: nestedRadius,\n aspectRatio: count === 1 ? '16/9' : isThirdImage ? '16/9' : '1',\n gridColumn: isThirdImage ? 'span 2' : undefined,\n }}\n >\n <img\n src={image}\n alt={`Post image ${index + 1}`}\n style={{\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n objectPosition: 'center',\n }}\n crossOrigin=\"anonymous\"\n referrerPolicy=\"no-referrer\"\n />\n {/* Inner stroke overlay */}\n <div\n style={{\n position: 'absolute',\n inset: 0,\n borderRadius: nestedRadius,\n boxShadow: `inset 0 0 0 1px ${theme.imageInnerStroke}`,\n pointerEvents: 'none',\n }}\n />\n </div>\n )\n })}\n </div>\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport * as React from 'react'\nimport { useState, useEffect } from 'react'\nimport type { XCardProps, PostData, Theme, ShadowIntensity } from './types'\nimport { getThemeStyles } from './themes'\nimport { PostCard, PostCardSkeleton } from './PostCard'\n\n/** Default API endpoint for fetching post data */\nconst DEFAULT_API_URL = 'https://x-post-card.vercel.app/api/scrape-post'\n\n/** Clamp a value between min and max */\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\n/** Default error display */\nfunction ErrorDisplay({\n error,\n width,\n radius,\n}: {\n error: Error\n width: number\n radius: number\n}) {\n return (\n <div\n style={{\n width,\n padding: 24,\n borderRadius: radius,\n backgroundColor: '#fef2f2',\n border: '1px solid #fecaca',\n color: '#dc2626',\n fontSize: 14,\n textAlign: 'center',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif',\n }}\n >\n <p style={{ margin: 0, fontWeight: 500 }}>Failed to load post</p>\n <p style={{ margin: '8px 0 0', opacity: 0.8, fontSize: 13 }}>{error.message}</p>\n </div>\n )\n}\n\n/**\n * XCard - Embed beautifully styled X (Twitter) post cards in your React app.\n *\n * @example\n * ```tsx\n * import { XCard } from 'x-post-card'\n *\n * function MyComponent() {\n * return (\n * <XCard\n * url=\"https://x.com/elonmusk/status/123456789\"\n * theme=\"dark\"\n * shadow=\"floating\"\n * width={450}\n * radius={20}\n * />\n * )\n * }\n * ```\n */\nexport function XCard({\n url,\n theme = 'light',\n shadow = 'floating',\n width = 450,\n radius = 20,\n apiUrl = DEFAULT_API_URL,\n className,\n fallback,\n onError,\n onLoad,\n}: XCardProps) {\n const [post, setPost] = useState<PostData | null>(null)\n const [error, setError] = useState<Error | null>(null)\n const [loading, setLoading] = useState(true)\n\n // Clamp values to valid ranges\n const clampedWidth = clamp(width, 350, 700)\n const clampedRadius = clamp(radius, 0, 24)\n\n // Get theme styles\n const themeStyles = getThemeStyles(theme as Theme)\n\n useEffect(() => {\n let cancelled = false\n\n async function fetchPost() {\n setLoading(true)\n setError(null)\n\n try {\n const response = await fetch(apiUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ url }),\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n throw new Error(errorData.error || `Failed to fetch post (${response.status})`)\n }\n\n const data = await response.json()\n\n if (cancelled) return\n\n setPost(data)\n onLoad?.(data)\n } catch (err) {\n if (cancelled) return\n\n const error = err instanceof Error ? err : new Error('Unknown error occurred')\n setError(error)\n onError?.(error)\n } finally {\n if (!cancelled) {\n setLoading(false)\n }\n }\n }\n\n fetchPost()\n\n return () => {\n cancelled = true\n }\n }, [url, apiUrl, onError, onLoad])\n\n // Loading state\n if (loading) {\n return (\n <div className={className}>\n {fallback || <PostCardSkeleton width={clampedWidth} radius={clampedRadius} />}\n </div>\n )\n }\n\n // Error state\n if (error) {\n return (\n <div className={className}>\n <ErrorDisplay error={error} width={clampedWidth} radius={clampedRadius} />\n </div>\n )\n }\n\n // Success state\n if (post) {\n return (\n <div className={className}>\n <PostCard\n post={post}\n theme={themeStyles}\n shadow={shadow as ShadowIntensity}\n width={clampedWidth}\n radius={clampedRadius}\n />\n </div>\n )\n }\n\n return null\n}\n"]}