@postrun/react 2.9.0 → 2.10.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.cjs CHANGED
@@ -1385,6 +1385,7 @@ function XPostPreviewImpl({
1385
1385
  resolvedQuotedMedia,
1386
1386
  replyToHandle
1387
1387
  ]);
1388
+ const isEmpty = !variant.body && resolvedMedia.length === 0 && !variant.settings?.poll && !tweet.quoted_tweet;
1388
1389
  return /* @__PURE__ */ jsxRuntime.jsx(
1389
1390
  "div",
1390
1391
  {
@@ -1394,7 +1395,7 @@ function XPostPreviewImpl({
1394
1395
  children: /* @__PURE__ */ jsxRuntime.jsxs(reactTweet.TweetContainer, { children: [
1395
1396
  /* @__PURE__ */ jsxRuntime.jsx(reactTweet.TweetHeader, { tweet, components }),
1396
1397
  tweet.in_reply_to_screen_name ? /* @__PURE__ */ jsxRuntime.jsx(reactTweet.TweetInReplyTo, { tweet }) : null,
1397
- /* @__PURE__ */ jsxRuntime.jsx(reactTweet.TweetBody, { tweet }),
1398
+ isEmpty ? /* @__PURE__ */ jsxRuntime.jsx(EmptyBody, {}) : /* @__PURE__ */ jsxRuntime.jsx(reactTweet.TweetBody, { tweet }),
1398
1399
  tweet.mediaDetails?.length ? /* @__PURE__ */ jsxRuntime.jsx(reactTweet.TweetMedia, { tweet, components }) : null,
1399
1400
  variant.settings?.poll ? /* @__PURE__ */ jsxRuntime.jsx(XPoll, { poll: variant.settings.poll }) : null,
1400
1401
  tweet.quoted_tweet ? /* @__PURE__ */ jsxRuntime.jsx(reactTweet.QuotedTweet, { tweet: tweet.quoted_tweet }) : null,
@@ -1403,6 +1404,18 @@ function XPostPreviewImpl({
1403
1404
  }
1404
1405
  );
1405
1406
  }
1407
+ function EmptyBody() {
1408
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { style: emptyBodyStyle, children: "What's happening?" });
1409
+ }
1410
+ var emptyBodyStyle = {
1411
+ margin: "var(--tweet-body-margin)",
1412
+ fontSize: "var(--tweet-body-font-size)",
1413
+ fontWeight: "var(--tweet-body-font-weight)",
1414
+ lineHeight: "var(--tweet-body-line-height)",
1415
+ color: "var(--tweet-font-color-secondary)",
1416
+ whiteSpace: "pre-wrap",
1417
+ overflowWrap: "break-word"
1418
+ };
1406
1419
  var XPostPreview = react.memo(XPostPreviewImpl);
1407
1420
 
1408
1421
  // src/preview/linkedin/theme.ts
@@ -1691,12 +1704,13 @@ function ImageMosaic({ media }) {
1691
1704
  return null;
1692
1705
  }
1693
1706
  if (media.length === 1) {
1707
+ const ratio = only.width && only.height ? `${only.width} / ${only.height}` : void 0;
1694
1708
  return /* @__PURE__ */ jsxRuntime.jsx(
1695
1709
  "img",
1696
1710
  {
1697
1711
  src: only.src,
1698
1712
  alt: only.alt ?? "",
1699
- style: { width: "100%", height: "auto", display: "block" }
1713
+ style: { width: "100%", height: "auto", display: "block", aspectRatio: ratio }
1700
1714
  }
1701
1715
  );
1702
1716
  }
@@ -1930,11 +1944,16 @@ function LinkedInPostPreviewImpl({
1930
1944
  color: varRef(LI_VAR.text),
1931
1945
  border: `1px solid ${varRef(LI_VAR.border)}`,
1932
1946
  borderRadius: 10,
1947
+ // Pin the width (see InstagramPostPreview): without it the card shrinks to its
1948
+ // content, so an empty post collapses to a fraction of a populated card's width.
1949
+ width: "100%",
1933
1950
  maxWidth: 552,
1951
+ boxSizing: "border-box",
1934
1952
  overflow: "hidden",
1935
1953
  fontFamily: FONT_STACK,
1936
1954
  ...style
1937
1955
  };
1956
+ const hasContent = Boolean(variant.body) || resolvedMedia.length > 0 || Boolean(variant.settings?.content_kind && variant.settings.content_kind !== "text");
1938
1957
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: cardStyle5, children: [
1939
1958
  /* @__PURE__ */ jsxRuntime.jsx(Header, { author, visibility, time }),
1940
1959
  variant.body ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px 16px 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -1946,9 +1965,16 @@ function LinkedInPostPreviewImpl({
1946
1965
  }
1947
1966
  ) }) : null,
1948
1967
  renderContent(variant, resolvedMedia),
1968
+ hasContent ? null : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { ...emptyBodyStyle2, color: BODY_COLORS.muted }, children: "What do you want to talk about?" }),
1949
1969
  showActions ? /* @__PURE__ */ jsxRuntime.jsx(EngagementBar, {}) : null
1950
1970
  ] });
1951
1971
  }
1972
+ var emptyBodyStyle2 = {
1973
+ margin: 0,
1974
+ padding: "8px 16px 4px",
1975
+ fontSize: 14,
1976
+ lineHeight: 1.4
1977
+ };
1952
1978
  function renderContent(variant, media) {
1953
1979
  const settings = variant.settings;
1954
1980
  const kind = settings?.content_kind;
@@ -2105,9 +2131,57 @@ var wrapStyle3 = {
2105
2131
  lineHeight: 1.4,
2106
2132
  color: varRef2(IG_VAR.text)
2107
2133
  };
2108
- function FeedMedia({ media }) {
2134
+ function MediaPlaceholder({
2135
+ label,
2136
+ color,
2137
+ background,
2138
+ shimmer = false
2139
+ }) {
2140
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...fillStyle, background, color }, children: [
2141
+ shimmer ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2142
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: SHIMMER_KEYFRAME }),
2143
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerStyle })
2144
+ ] }) : null,
2145
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle, children: label })
2146
+ ] });
2147
+ }
2148
+ var SHIMMER_KEYFRAME = "@keyframes pr-media-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}";
2149
+ var fillStyle = {
2150
+ position: "absolute",
2151
+ inset: 0,
2152
+ display: "flex",
2153
+ alignItems: "center",
2154
+ justifyContent: "center",
2155
+ overflow: "hidden"
2156
+ };
2157
+ var labelStyle = {
2158
+ position: "relative",
2159
+ zIndex: 1,
2160
+ fontSize: 13,
2161
+ letterSpacing: 0.1
2162
+ };
2163
+ var shimmerStyle = {
2164
+ position: "absolute",
2165
+ inset: 0,
2166
+ background: "linear-gradient(90deg, transparent 0%, color-mix(in srgb, currentColor 12%, transparent) 50%, transparent 100%)",
2167
+ backgroundSize: "200% 100%",
2168
+ animation: "pr-media-shimmer 1.4s ease-in-out infinite"
2169
+ };
2170
+ function FeedMedia({
2171
+ media,
2172
+ pending = false
2173
+ }) {
2109
2174
  if (media.length <= 1) {
2110
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: frameStyle, children: media[0] ? /* @__PURE__ */ jsxRuntime.jsx(Tile2, { item: media[0] }) : null });
2175
+ const item = media[0];
2176
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: frameStyle, children: item ? /* @__PURE__ */ jsxRuntime.jsx(Tile2, { item }) : /* @__PURE__ */ jsxRuntime.jsx(
2177
+ MediaPlaceholder,
2178
+ {
2179
+ label: pending ? "Processing media\u2026" : "No media yet",
2180
+ color: varRef2(IG_VAR.muted),
2181
+ background: EMPTY_BG,
2182
+ shimmer: pending
2183
+ }
2184
+ ) });
2111
2185
  }
2112
2186
  return /* @__PURE__ */ jsxRuntime.jsx(Carousel, { media });
2113
2187
  }
@@ -2156,6 +2230,7 @@ var frameStyle = {
2156
2230
  background: "#000",
2157
2231
  overflow: "hidden"
2158
2232
  };
2233
+ var EMPTY_BG = "light-dark(#efefef, #1c1c1c)";
2159
2234
  var mediaStyle = {
2160
2235
  position: "absolute",
2161
2236
  inset: 0,
@@ -2270,6 +2345,7 @@ function ReelPreview({
2270
2345
  author,
2271
2346
  body,
2272
2347
  media,
2348
+ pending = false,
2273
2349
  audioName,
2274
2350
  className,
2275
2351
  style
@@ -2288,7 +2364,15 @@ function ReelPreview({
2288
2364
  playsInline: true,
2289
2365
  style: coverStyle2
2290
2366
  }
2291
- ) : /* @__PURE__ */ jsxRuntime.jsx("img", { src: first.src, alt: first.alt ?? "", style: coverStyle2 }) : null,
2367
+ ) : /* @__PURE__ */ jsxRuntime.jsx("img", { src: first.src, alt: first.alt ?? "", style: coverStyle2 }) : /* @__PURE__ */ jsxRuntime.jsx(
2368
+ MediaPlaceholder,
2369
+ {
2370
+ label: pending ? "Processing media\u2026" : "No media yet",
2371
+ color: "rgba(255,255,255,0.55)",
2372
+ background: "transparent",
2373
+ shimmer: pending
2374
+ }
2375
+ ),
2292
2376
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: scrimStyle }),
2293
2377
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: infoStyle, children: [
2294
2378
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: authorRowStyle, children: [
@@ -2393,6 +2477,7 @@ function InstagramPostPreviewImpl({
2393
2477
  );
2394
2478
  const settings = variant.settings;
2395
2479
  const isReel = variant.post_type === "reel";
2480
+ const pending = (media?.length ?? 0) > 0 && resolvedMedia.length === 0;
2396
2481
  if (isReel) {
2397
2482
  return /* @__PURE__ */ jsxRuntime.jsx(
2398
2483
  ReelPreview,
@@ -2400,6 +2485,7 @@ function InstagramPostPreviewImpl({
2400
2485
  author,
2401
2486
  body: variant.body ?? "",
2402
2487
  media: resolvedMedia,
2488
+ pending,
2403
2489
  audioName: settings?.audio_name,
2404
2490
  className,
2405
2491
  style
@@ -2414,14 +2500,20 @@ function InstagramPostPreviewImpl({
2414
2500
  color: varRef2(IG_VAR.text),
2415
2501
  border: `1px solid ${varRef2(IG_VAR.border)}`,
2416
2502
  borderRadius: 8,
2503
+ // Fill the container up to IG's feed width. WITHOUT an explicit width the card
2504
+ // shrinks to its content, so an empty post (no caption/media to widen it) would
2505
+ // collapse to a fraction of a populated card's width — a jarring size change as
2506
+ // you type. Pinning width keeps every state the SAME size (zero layout shift).
2507
+ width: "100%",
2417
2508
  maxWidth: 470,
2509
+ boxSizing: "border-box",
2418
2510
  overflow: "hidden",
2419
2511
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
2420
2512
  ...style
2421
2513
  };
2422
2514
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: cardStyle5, children: [
2423
2515
  /* @__PURE__ */ jsxRuntime.jsx(Header2, { author, collaborators }),
2424
- /* @__PURE__ */ jsxRuntime.jsx(FeedMedia, { media: resolvedMedia }),
2516
+ /* @__PURE__ */ jsxRuntime.jsx(FeedMedia, { media: resolvedMedia, pending }),
2425
2517
  /* @__PURE__ */ jsxRuntime.jsx(Actions, {}),
2426
2518
  variant.body ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingBottom: 12 }, children: /* @__PURE__ */ jsxRuntime.jsx(Caption, { username: author.username, body: variant.body }) }) : null
2427
2519
  ] });
@@ -2446,7 +2538,15 @@ function Media2({
2446
2538
  }) {
2447
2539
  const [first] = media;
2448
2540
  if (!first) {
2449
- return pending ? /* @__PURE__ */ jsxRuntime.jsx(ProcessingMedia, {}) : /* @__PURE__ */ jsxRuntime.jsx(EmptyMedia, {});
2541
+ return /* @__PURE__ */ jsxRuntime.jsx(
2542
+ MediaPlaceholder,
2543
+ {
2544
+ label: pending ? "Processing media\u2026" : "No media yet",
2545
+ color: "rgba(255,255,255,0.55)",
2546
+ background: "linear-gradient(180deg,#1c1c20,#101013)",
2547
+ shimmer: pending
2548
+ }
2549
+ );
2450
2550
  }
2451
2551
  if (first.kind === "video") {
2452
2552
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -2495,17 +2595,6 @@ function PhotoCarousel({ media }) {
2495
2595
  )) }) : null
2496
2596
  ] });
2497
2597
  }
2498
- function EmptyMedia() {
2499
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: emptyStyle, children: "No media yet" });
2500
- }
2501
- function ProcessingMedia() {
2502
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: emptyStyle, children: [
2503
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: SHIMMER_KEYFRAME }),
2504
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: shimmerStyle }),
2505
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { position: "relative", zIndex: 1 }, children: "Processing media\u2026" })
2506
- ] });
2507
- }
2508
- var SHIMMER_KEYFRAME = `@keyframes pr-tt-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}`;
2509
2598
  var coverStyle3 = {
2510
2599
  position: "absolute",
2511
2600
  inset: 0,
@@ -2544,24 +2633,6 @@ var dotStyle2 = {
2544
2633
  background: "#fff",
2545
2634
  transition: "opacity 160ms ease"
2546
2635
  };
2547
- var emptyStyle = {
2548
- position: "absolute",
2549
- inset: 0,
2550
- display: "flex",
2551
- alignItems: "center",
2552
- justifyContent: "center",
2553
- color: "rgba(255,255,255,0.55)",
2554
- fontSize: 13,
2555
- background: "linear-gradient(180deg,#1c1c20,#101013)",
2556
- overflow: "hidden"
2557
- };
2558
- var shimmerStyle = {
2559
- position: "absolute",
2560
- inset: 0,
2561
- background: "linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0) 100%)",
2562
- backgroundSize: "200% 100%",
2563
- animation: "pr-tt-shimmer 1.4s ease-in-out infinite"
2564
- };
2565
2636
  var Icon = ({
2566
2637
  size = 24,
2567
2638
  viewBox,
@@ -2809,7 +2880,7 @@ var discImgStyle = {
2809
2880
  borderRadius: "50%",
2810
2881
  objectFit: "cover"
2811
2882
  };
2812
- 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}}`;
2883
+ var KEYFRAMES = `@keyframes pr-tt-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}`;
2813
2884
  var TikTokPostPreview = react.memo(TikTokPostPreviewImpl);
2814
2885
 
2815
2886
  // src/preview/tiktok/theme.ts
@@ -3031,7 +3102,7 @@ function TikTokCaptionField({
3031
3102
  const over = count > max;
3032
3103
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...wrapStyle5, ...style }, children: [
3033
3104
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: labelRowStyle, children: [
3034
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, style: labelStyle, children: "Caption" }),
3105
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, style: labelStyle2, children: "Caption" }),
3035
3106
  /* @__PURE__ */ jsxRuntime.jsxs(
3036
3107
  "span",
3037
3108
  {
@@ -3074,7 +3145,7 @@ var labelRowStyle = {
3074
3145
  alignItems: "baseline",
3075
3146
  justifyContent: "space-between"
3076
3147
  };
3077
- var labelStyle = {
3148
+ var labelStyle2 = {
3078
3149
  fontSize: 13,
3079
3150
  fontWeight: 600,
3080
3151
  color: "inherit"