@postrun/react 2.9.0 → 2.11.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/dist/index.js CHANGED
@@ -1374,6 +1374,7 @@ function XPostPreviewImpl({
1374
1374
  resolvedQuotedMedia,
1375
1375
  replyToHandle
1376
1376
  ]);
1377
+ const isEmpty = !variant.body && resolvedMedia.length === 0 && !variant.settings?.poll && !tweet.quoted_tweet;
1377
1378
  return /* @__PURE__ */ jsx(
1378
1379
  "div",
1379
1380
  {
@@ -1383,7 +1384,7 @@ function XPostPreviewImpl({
1383
1384
  children: /* @__PURE__ */ jsxs(TweetContainer, { children: [
1384
1385
  /* @__PURE__ */ jsx(TweetHeader, { tweet, components }),
1385
1386
  tweet.in_reply_to_screen_name ? /* @__PURE__ */ jsx(TweetInReplyTo, { tweet }) : null,
1386
- /* @__PURE__ */ jsx(TweetBody, { tweet }),
1387
+ isEmpty ? /* @__PURE__ */ jsx(EmptyBody, {}) : /* @__PURE__ */ jsx(TweetBody, { tweet }),
1387
1388
  tweet.mediaDetails?.length ? /* @__PURE__ */ jsx(TweetMedia, { tweet, components }) : null,
1388
1389
  variant.settings?.poll ? /* @__PURE__ */ jsx(XPoll, { poll: variant.settings.poll }) : null,
1389
1390
  tweet.quoted_tweet ? /* @__PURE__ */ jsx(QuotedTweet, { tweet: tweet.quoted_tweet }) : null,
@@ -1392,6 +1393,18 @@ function XPostPreviewImpl({
1392
1393
  }
1393
1394
  );
1394
1395
  }
1396
+ function EmptyBody() {
1397
+ return /* @__PURE__ */ jsx("p", { style: emptyBodyStyle, children: "What's happening?" });
1398
+ }
1399
+ var emptyBodyStyle = {
1400
+ margin: "var(--tweet-body-margin)",
1401
+ fontSize: "var(--tweet-body-font-size)",
1402
+ fontWeight: "var(--tweet-body-font-weight)",
1403
+ lineHeight: "var(--tweet-body-line-height)",
1404
+ color: "var(--tweet-font-color-secondary)",
1405
+ whiteSpace: "pre-wrap",
1406
+ overflowWrap: "break-word"
1407
+ };
1395
1408
  var XPostPreview = memo(XPostPreviewImpl);
1396
1409
 
1397
1410
  // src/preview/linkedin/theme.ts
@@ -1680,12 +1693,13 @@ function ImageMosaic({ media }) {
1680
1693
  return null;
1681
1694
  }
1682
1695
  if (media.length === 1) {
1696
+ const ratio = only.width && only.height ? `${only.width} / ${only.height}` : void 0;
1683
1697
  return /* @__PURE__ */ jsx(
1684
1698
  "img",
1685
1699
  {
1686
1700
  src: only.src,
1687
1701
  alt: only.alt ?? "",
1688
- style: { width: "100%", height: "auto", display: "block" }
1702
+ style: { width: "100%", height: "auto", display: "block", aspectRatio: ratio }
1689
1703
  }
1690
1704
  );
1691
1705
  }
@@ -1919,11 +1933,16 @@ function LinkedInPostPreviewImpl({
1919
1933
  color: varRef(LI_VAR.text),
1920
1934
  border: `1px solid ${varRef(LI_VAR.border)}`,
1921
1935
  borderRadius: 10,
1936
+ // Pin the width (see InstagramPostPreview): without it the card shrinks to its
1937
+ // content, so an empty post collapses to a fraction of a populated card's width.
1938
+ width: "100%",
1922
1939
  maxWidth: 552,
1940
+ boxSizing: "border-box",
1923
1941
  overflow: "hidden",
1924
1942
  fontFamily: FONT_STACK,
1925
1943
  ...style
1926
1944
  };
1945
+ const hasContent = Boolean(variant.body) || resolvedMedia.length > 0 || Boolean(variant.settings?.content_kind && variant.settings.content_kind !== "text");
1927
1946
  return /* @__PURE__ */ jsxs("div", { className, style: cardStyle5, children: [
1928
1947
  /* @__PURE__ */ jsx(Header, { author, visibility, time }),
1929
1948
  variant.body ? /* @__PURE__ */ jsx("div", { style: { padding: "8px 16px 0" }, children: /* @__PURE__ */ jsx(
@@ -1935,9 +1954,16 @@ function LinkedInPostPreviewImpl({
1935
1954
  }
1936
1955
  ) }) : null,
1937
1956
  renderContent(variant, resolvedMedia),
1957
+ hasContent ? null : /* @__PURE__ */ jsx("p", { style: { ...emptyBodyStyle2, color: BODY_COLORS.muted }, children: "What do you want to talk about?" }),
1938
1958
  showActions ? /* @__PURE__ */ jsx(EngagementBar, {}) : null
1939
1959
  ] });
1940
1960
  }
1961
+ var emptyBodyStyle2 = {
1962
+ margin: 0,
1963
+ padding: "8px 16px 4px",
1964
+ fontSize: 14,
1965
+ lineHeight: 1.4
1966
+ };
1941
1967
  function renderContent(variant, media) {
1942
1968
  const settings = variant.settings;
1943
1969
  const kind = settings?.content_kind;
@@ -2094,9 +2120,57 @@ var wrapStyle3 = {
2094
2120
  lineHeight: 1.4,
2095
2121
  color: varRef2(IG_VAR.text)
2096
2122
  };
2097
- function FeedMedia({ media }) {
2123
+ function MediaPlaceholder({
2124
+ label,
2125
+ color,
2126
+ background,
2127
+ shimmer = false
2128
+ }) {
2129
+ return /* @__PURE__ */ jsxs("div", { style: { ...fillStyle, background, color }, children: [
2130
+ shimmer ? /* @__PURE__ */ jsxs(Fragment, { children: [
2131
+ /* @__PURE__ */ jsx("style", { children: SHIMMER_KEYFRAME }),
2132
+ /* @__PURE__ */ jsx("div", { style: shimmerStyle })
2133
+ ] }) : null,
2134
+ /* @__PURE__ */ jsx("span", { style: labelStyle, children: label })
2135
+ ] });
2136
+ }
2137
+ var SHIMMER_KEYFRAME = "@keyframes pr-media-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}";
2138
+ var fillStyle = {
2139
+ position: "absolute",
2140
+ inset: 0,
2141
+ display: "flex",
2142
+ alignItems: "center",
2143
+ justifyContent: "center",
2144
+ overflow: "hidden"
2145
+ };
2146
+ var labelStyle = {
2147
+ position: "relative",
2148
+ zIndex: 1,
2149
+ fontSize: 13,
2150
+ letterSpacing: 0.1
2151
+ };
2152
+ var shimmerStyle = {
2153
+ position: "absolute",
2154
+ inset: 0,
2155
+ background: "linear-gradient(90deg, transparent 0%, color-mix(in srgb, currentColor 12%, transparent) 50%, transparent 100%)",
2156
+ backgroundSize: "200% 100%",
2157
+ animation: "pr-media-shimmer 1.4s ease-in-out infinite"
2158
+ };
2159
+ function FeedMedia({
2160
+ media,
2161
+ pending = false
2162
+ }) {
2098
2163
  if (media.length <= 1) {
2099
- return /* @__PURE__ */ jsx("div", { style: frameStyle, children: media[0] ? /* @__PURE__ */ jsx(Tile2, { item: media[0] }) : null });
2164
+ const item = media[0];
2165
+ return /* @__PURE__ */ jsx("div", { style: frameStyle, children: item ? /* @__PURE__ */ jsx(Tile2, { item }) : /* @__PURE__ */ jsx(
2166
+ MediaPlaceholder,
2167
+ {
2168
+ label: pending ? "Processing media\u2026" : "No media yet",
2169
+ color: varRef2(IG_VAR.muted),
2170
+ background: EMPTY_BG,
2171
+ shimmer: pending
2172
+ }
2173
+ ) });
2100
2174
  }
2101
2175
  return /* @__PURE__ */ jsx(Carousel, { media });
2102
2176
  }
@@ -2145,6 +2219,7 @@ var frameStyle = {
2145
2219
  background: "#000",
2146
2220
  overflow: "hidden"
2147
2221
  };
2222
+ var EMPTY_BG = "light-dark(#efefef, #1c1c1c)";
2148
2223
  var mediaStyle = {
2149
2224
  position: "absolute",
2150
2225
  inset: 0,
@@ -2259,6 +2334,7 @@ function ReelPreview({
2259
2334
  author,
2260
2335
  body,
2261
2336
  media,
2337
+ pending = false,
2262
2338
  audioName,
2263
2339
  className,
2264
2340
  style
@@ -2277,7 +2353,15 @@ function ReelPreview({
2277
2353
  playsInline: true,
2278
2354
  style: coverStyle2
2279
2355
  }
2280
- ) : /* @__PURE__ */ jsx("img", { src: first.src, alt: first.alt ?? "", style: coverStyle2 }) : null,
2356
+ ) : /* @__PURE__ */ jsx("img", { src: first.src, alt: first.alt ?? "", style: coverStyle2 }) : /* @__PURE__ */ jsx(
2357
+ MediaPlaceholder,
2358
+ {
2359
+ label: pending ? "Processing media\u2026" : "No media yet",
2360
+ color: "rgba(255,255,255,0.55)",
2361
+ background: "transparent",
2362
+ shimmer: pending
2363
+ }
2364
+ ),
2281
2365
  /* @__PURE__ */ jsx("div", { style: scrimStyle }),
2282
2366
  /* @__PURE__ */ jsxs("div", { style: infoStyle, children: [
2283
2367
  /* @__PURE__ */ jsxs("div", { style: authorRowStyle, children: [
@@ -2382,6 +2466,7 @@ function InstagramPostPreviewImpl({
2382
2466
  );
2383
2467
  const settings = variant.settings;
2384
2468
  const isReel = variant.post_type === "reel";
2469
+ const pending = (media?.length ?? 0) > 0 && resolvedMedia.length === 0;
2385
2470
  if (isReel) {
2386
2471
  return /* @__PURE__ */ jsx(
2387
2472
  ReelPreview,
@@ -2389,6 +2474,7 @@ function InstagramPostPreviewImpl({
2389
2474
  author,
2390
2475
  body: variant.body ?? "",
2391
2476
  media: resolvedMedia,
2477
+ pending,
2392
2478
  audioName: settings?.audio_name,
2393
2479
  className,
2394
2480
  style
@@ -2403,14 +2489,20 @@ function InstagramPostPreviewImpl({
2403
2489
  color: varRef2(IG_VAR.text),
2404
2490
  border: `1px solid ${varRef2(IG_VAR.border)}`,
2405
2491
  borderRadius: 8,
2492
+ // Fill the container up to IG's feed width. WITHOUT an explicit width the card
2493
+ // shrinks to its content, so an empty post (no caption/media to widen it) would
2494
+ // collapse to a fraction of a populated card's width — a jarring size change as
2495
+ // you type. Pinning width keeps every state the SAME size (zero layout shift).
2496
+ width: "100%",
2406
2497
  maxWidth: 470,
2498
+ boxSizing: "border-box",
2407
2499
  overflow: "hidden",
2408
2500
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
2409
2501
  ...style
2410
2502
  };
2411
2503
  return /* @__PURE__ */ jsxs("div", { className, style: cardStyle5, children: [
2412
2504
  /* @__PURE__ */ jsx(Header2, { author, collaborators }),
2413
- /* @__PURE__ */ jsx(FeedMedia, { media: resolvedMedia }),
2505
+ /* @__PURE__ */ jsx(FeedMedia, { media: resolvedMedia, pending }),
2414
2506
  /* @__PURE__ */ jsx(Actions, {}),
2415
2507
  variant.body ? /* @__PURE__ */ jsx("div", { style: { paddingBottom: 12 }, children: /* @__PURE__ */ jsx(Caption, { username: author.username, body: variant.body }) }) : null
2416
2508
  ] });
@@ -2435,7 +2527,15 @@ function Media2({
2435
2527
  }) {
2436
2528
  const [first] = media;
2437
2529
  if (!first) {
2438
- return pending ? /* @__PURE__ */ jsx(ProcessingMedia, {}) : /* @__PURE__ */ jsx(EmptyMedia, {});
2530
+ return /* @__PURE__ */ jsx(
2531
+ MediaPlaceholder,
2532
+ {
2533
+ label: pending ? "Processing media\u2026" : "No media yet",
2534
+ color: "rgba(255,255,255,0.55)",
2535
+ background: "linear-gradient(180deg,#1c1c20,#101013)",
2536
+ shimmer: pending
2537
+ }
2538
+ );
2439
2539
  }
2440
2540
  if (first.kind === "video") {
2441
2541
  return /* @__PURE__ */ jsx(
@@ -2484,17 +2584,6 @@ function PhotoCarousel({ media }) {
2484
2584
  )) }) : null
2485
2585
  ] });
2486
2586
  }
2487
- function EmptyMedia() {
2488
- return /* @__PURE__ */ jsx("div", { style: emptyStyle, children: "No media yet" });
2489
- }
2490
- function ProcessingMedia() {
2491
- return /* @__PURE__ */ jsxs("div", { style: emptyStyle, children: [
2492
- /* @__PURE__ */ jsx("style", { children: SHIMMER_KEYFRAME }),
2493
- /* @__PURE__ */ jsx("div", { style: shimmerStyle }),
2494
- /* @__PURE__ */ jsx("span", { style: { position: "relative", zIndex: 1 }, children: "Processing media\u2026" })
2495
- ] });
2496
- }
2497
- var SHIMMER_KEYFRAME = `@keyframes pr-tt-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`;
2498
2587
  var coverStyle3 = {
2499
2588
  position: "absolute",
2500
2589
  inset: 0,
@@ -2533,24 +2622,6 @@ var dotStyle2 = {
2533
2622
  background: "#fff",
2534
2623
  transition: "opacity 160ms ease"
2535
2624
  };
2536
- var emptyStyle = {
2537
- position: "absolute",
2538
- inset: 0,
2539
- display: "flex",
2540
- alignItems: "center",
2541
- justifyContent: "center",
2542
- color: "rgba(255,255,255,0.55)",
2543
- fontSize: 13,
2544
- background: "linear-gradient(180deg,#1c1c20,#101013)",
2545
- overflow: "hidden"
2546
- };
2547
- var shimmerStyle = {
2548
- position: "absolute",
2549
- inset: 0,
2550
- background: "linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0) 100%)",
2551
- backgroundSize: "200% 100%",
2552
- animation: "pr-tt-shimmer 1.4s ease-in-out infinite"
2553
- };
2554
2625
  var Icon = ({
2555
2626
  size = 24,
2556
2627
  viewBox,
@@ -2798,7 +2869,7 @@ var discImgStyle = {
2798
2869
  borderRadius: "50%",
2799
2870
  objectFit: "cover"
2800
2871
  };
2801
- var KEYFRAMES = `@keyframes pr-tt-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pr-tt-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`;
2872
+ var KEYFRAMES = `@keyframes pr-tt-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}`;
2802
2873
  var TikTokPostPreview = memo(TikTokPostPreviewImpl);
2803
2874
 
2804
2875
  // src/preview/tiktok/theme.ts
@@ -3020,7 +3091,7 @@ function TikTokCaptionField({
3020
3091
  const over = count > max;
3021
3092
  return /* @__PURE__ */ jsxs("div", { className, style: { ...wrapStyle5, ...style }, children: [
3022
3093
  /* @__PURE__ */ jsxs("div", { style: labelRowStyle, children: [
3023
- /* @__PURE__ */ jsx("label", { htmlFor: id, style: labelStyle, children: "Caption" }),
3094
+ /* @__PURE__ */ jsx("label", { htmlFor: id, style: labelStyle2, children: "Caption" }),
3024
3095
  /* @__PURE__ */ jsxs(
3025
3096
  "span",
3026
3097
  {
@@ -3063,7 +3134,7 @@ var labelRowStyle = {
3063
3134
  alignItems: "baseline",
3064
3135
  justifyContent: "space-between"
3065
3136
  };
3066
- var labelStyle = {
3137
+ var labelStyle2 = {
3067
3138
  fontSize: 13,
3068
3139
  fontWeight: 600,
3069
3140
  color: "inherit"