@opensite/ui 3.11.0 → 3.12.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.
@@ -423,6 +423,39 @@ var Section = React__namespace.default.forwardRef(
423
423
  Section.displayName = "Section";
424
424
  var IG_TILE_WIDTH = 320;
425
425
  var IG_TILE_STYLE = { width: "100%" };
426
+ var IG_TILE_RADIUS = 18;
427
+ var TILE_WRAPPER_STYLE = {
428
+ position: "relative",
429
+ borderRadius: IG_TILE_RADIUS,
430
+ boxShadow: "0 1px 2px rgba(15, 23, 42, 0.08), 0 6px 16px rgba(15, 23, 42, 0.10)"
431
+ };
432
+ var CAPTION_SCRIM_STYLE = {
433
+ position: "absolute",
434
+ left: 0,
435
+ right: 0,
436
+ bottom: 0,
437
+ padding: "28px 14px 13px",
438
+ background: "linear-gradient(180deg, rgba(8,12,24,0) 0%, rgba(8,12,24,0.55) 45%, rgba(8,12,24,0.78) 100%)",
439
+ borderBottomLeftRadius: IG_TILE_RADIUS,
440
+ borderBottomRightRadius: IG_TILE_RADIUS,
441
+ pointerEvents: "none",
442
+ // Hiding captions for now until we build in
443
+ // the dynamic behavior that clients can dictate
444
+ display: "none"
445
+ };
446
+ var CAPTION_TEXT_STYLE = {
447
+ color: "#fff",
448
+ fontSize: 13,
449
+ lineHeight: 1.4,
450
+ fontWeight: 600,
451
+ letterSpacing: "0.005em",
452
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.35)",
453
+ display: "-webkit-box",
454
+ WebkitLineClamp: 2,
455
+ WebkitBoxOrient: "vertical",
456
+ overflow: "hidden",
457
+ height: "2.8em"
458
+ };
426
459
  var TITLE_MAX = 90;
427
460
  function truncate(text, max = TITLE_MAX) {
428
461
  const trimmed = text.trim();
@@ -472,19 +505,23 @@ function likeBadge(likeCount) {
472
505
  style: {
473
506
  display: "inline-flex",
474
507
  alignItems: "center",
475
- gap: 4,
476
- padding: "3px 8px",
508
+ gap: 6,
509
+ // Breathing room from the tile's top-left corner (refinement #1).
510
+ margin: 6,
511
+ padding: "6px 11px",
477
512
  borderRadius: 999,
478
- background: "rgba(8,12,24,0.65)",
479
- backdropFilter: "blur(6px)",
480
- WebkitBackdropFilter: "blur(6px)",
513
+ background: "rgba(8, 12, 24, 0.55)",
514
+ backdropFilter: "blur(8px)",
515
+ WebkitBackdropFilter: "blur(8px)",
516
+ border: "1px solid rgba(255, 255, 255, 0.14)",
481
517
  color: "#fff",
482
- fontSize: 11,
483
- fontWeight: 700,
484
- lineHeight: 1
518
+ fontSize: 13,
519
+ fontWeight: 600,
520
+ lineHeight: 1,
521
+ letterSpacing: "0.01em"
485
522
  },
486
523
  children: [
487
- /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/heart", size: 13, "aria-hidden": "true" }),
524
+ /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "mdi/cards-heart", size: 15, "aria-hidden": "true" }),
488
525
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: likeCount.toLocaleString() })
489
526
  ]
490
527
  }
@@ -493,157 +530,148 @@ function likeBadge(likeCount) {
493
530
  var VIEWER_ACTIONS = [
494
531
  {
495
532
  id: "open-in-instagram",
496
- icon: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/external-link", size: 22, "aria-hidden": "true" }),
533
+ icon: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/instagram", size: 22, "aria-hidden": "true" }),
497
534
  label: "Instagram",
498
535
  ariaLabel: "Open in Instagram",
499
536
  onPress: (item) => openPermalink(item)
500
537
  }
501
538
  ];
502
- function RailStat({
503
- iconName,
504
- count,
505
- label,
506
- onPress
507
- }) {
508
- return /* @__PURE__ */ jsxRuntime.jsxs(
509
- "button",
539
+ function InstagramViewerRail({ item }) {
540
+ const meta = item.meta ?? {};
541
+ return /* @__PURE__ */ jsxRuntime.jsx(
542
+ "div",
510
543
  {
511
- type: "button",
512
- "aria-label": `${count.toLocaleString()} ${label}`,
513
- onClick: (e) => {
514
- e.stopPropagation();
515
- onPress();
516
- },
517
544
  style: {
545
+ position: "absolute",
546
+ right: 11,
547
+ bottom: 135,
518
548
  display: "flex",
519
549
  flexDirection: "column",
520
550
  alignItems: "center",
521
- gap: 5,
522
- padding: 0,
523
- border: "none",
524
- background: "transparent",
525
- color: "inherit",
526
- cursor: "pointer",
527
- font: "inherit"
551
+ gap: 18,
552
+ color: "var(--psmi-chrome-fg, #fff)",
553
+ zIndex: 3
528
554
  },
529
- children: [
530
- /* @__PURE__ */ jsxRuntime.jsx(
531
- "span",
532
- {
533
- style: {
534
- width: 46,
535
- height: 46,
536
- borderRadius: "50%",
537
- background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
538
- backdropFilter: "blur(6px)",
539
- WebkitBackdropFilter: "blur(6px)",
540
- display: "flex",
541
- alignItems: "center",
542
- justifyContent: "center"
543
- },
544
- children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: iconName, size: 22, "aria-hidden": "true" })
545
- }
546
- ),
547
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { fontSize: 11, fontWeight: 600 }, children: count.toLocaleString() })
548
- ]
555
+ children: meta.href ? /* @__PURE__ */ jsxRuntime.jsx(
556
+ "a",
557
+ {
558
+ href: meta.href,
559
+ target: "_blank",
560
+ rel: "noopener noreferrer",
561
+ "aria-label": "Open in Instagram",
562
+ onClick: (e) => e.stopPropagation(),
563
+ style: {
564
+ display: "flex",
565
+ flexDirection: "column",
566
+ alignItems: "center",
567
+ padding: 0,
568
+ border: "none",
569
+ background: "transparent",
570
+ color: "inherit",
571
+ cursor: "pointer",
572
+ font: "inherit",
573
+ textDecoration: "none"
574
+ },
575
+ children: /* @__PURE__ */ jsxRuntime.jsx(
576
+ "span",
577
+ {
578
+ style: {
579
+ width: 46,
580
+ height: 46,
581
+ borderRadius: "50%",
582
+ background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
583
+ backdropFilter: "blur(6px)",
584
+ WebkitBackdropFilter: "blur(6px)",
585
+ display: "flex",
586
+ alignItems: "center",
587
+ justifyContent: "center"
588
+ },
589
+ children: /* @__PURE__ */ jsxRuntime.jsx(
590
+ DynamicIcon,
591
+ {
592
+ name: "lucide/instagram",
593
+ size: 22,
594
+ "aria-hidden": "true"
595
+ }
596
+ )
597
+ }
598
+ )
599
+ }
600
+ ) : null
549
601
  }
550
602
  );
551
603
  }
552
- function InstagramViewerRail({ item }) {
553
- const meta = item.meta ?? {};
554
- const open = () => openPermalink(item);
604
+ function profileUrl(username) {
605
+ return `https://www.instagram.com/${username.replace(/^@+/, "")}/`;
606
+ }
607
+ var USERNAME_BADGE_STYLE = {
608
+ display: "inline-flex",
609
+ alignItems: "center",
610
+ gap: 7,
611
+ marginBottom: 10,
612
+ padding: "7px 12px",
613
+ borderRadius: 999,
614
+ background: "var(--psmi-chrome-bg, rgba(255,255,255,0.16))",
615
+ backdropFilter: "blur(8px)",
616
+ WebkitBackdropFilter: "blur(8px)",
617
+ border: "1px solid rgba(255, 255, 255, 0.18)",
618
+ color: "inherit",
619
+ fontSize: 13,
620
+ fontWeight: 700,
621
+ lineHeight: 1,
622
+ letterSpacing: "0.01em",
623
+ textDecoration: "none",
624
+ pointerEvents: "auto"
625
+ };
626
+ var VIEWER_TITLE_STYLE = {
627
+ fontSize: 16,
628
+ fontWeight: 700,
629
+ lineHeight: 1.35,
630
+ textShadow: "0 1px 2px rgba(0, 0, 0, 0.4)",
631
+ display: "-webkit-box",
632
+ WebkitLineClamp: 2,
633
+ WebkitBoxOrient: "vertical",
634
+ overflow: "hidden"
635
+ };
636
+ function InstagramViewerCaption({
637
+ item,
638
+ username
639
+ }) {
640
+ const handle = username?.replace(/^@+/, "");
641
+ const badgeContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
642
+ /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/instagram", size: 14, "aria-hidden": "true" }),
643
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: handle ? `@${handle}` : "Instagram" })
644
+ ] });
555
645
  return /* @__PURE__ */ jsxRuntime.jsxs(
556
646
  "div",
557
647
  {
558
648
  style: {
559
649
  position: "absolute",
560
- right: 11,
561
- bottom: 135,
562
- display: "flex",
563
- flexDirection: "column",
564
- alignItems: "center",
565
- gap: 18,
650
+ left: 16,
651
+ // Mirrors the library caption card's clearance for the actions rail.
652
+ right: 78,
653
+ bottom: 34,
566
654
  color: "var(--psmi-chrome-fg, #fff)",
567
- zIndex: 3
655
+ zIndex: 2
568
656
  },
569
657
  children: [
570
- typeof meta.likeCount === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
571
- RailStat,
572
- {
573
- iconName: "lucide/heart",
574
- count: meta.likeCount,
575
- label: "likes",
576
- onPress: open
577
- }
578
- ) : null,
579
- typeof meta.commentCount === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
580
- RailStat,
581
- {
582
- iconName: "lucide/message-circle",
583
- count: meta.commentCount,
584
- label: "comments",
585
- onPress: open
586
- }
587
- ) : null,
588
- typeof meta.viewCount === "number" ? /* @__PURE__ */ jsxRuntime.jsx(
589
- RailStat,
590
- {
591
- iconName: "lucide/eye",
592
- count: meta.viewCount,
593
- label: "views",
594
- onPress: open
595
- }
596
- ) : null,
597
- meta.href ? /* @__PURE__ */ jsxRuntime.jsxs(
658
+ handle ? /* @__PURE__ */ jsxRuntime.jsx(
598
659
  "a",
599
660
  {
600
- href: meta.href,
661
+ href: profileUrl(handle),
601
662
  target: "_blank",
602
663
  rel: "noopener noreferrer",
603
- "aria-label": "Open in Instagram",
664
+ "aria-label": `Open @${handle} on Instagram`,
604
665
  onClick: (e) => e.stopPropagation(),
605
- style: {
606
- display: "flex",
607
- flexDirection: "column",
608
- alignItems: "center",
609
- gap: 5,
610
- padding: 0,
611
- border: "none",
612
- background: "transparent",
613
- color: "inherit",
614
- cursor: "pointer",
615
- font: "inherit",
616
- textDecoration: "none"
617
- },
618
- children: [
619
- /* @__PURE__ */ jsxRuntime.jsx(
620
- "span",
621
- {
622
- style: {
623
- width: 46,
624
- height: 46,
625
- borderRadius: "50%",
626
- background: "var(--psmi-chrome-bg, rgba(255,255,255,0.14))",
627
- backdropFilter: "blur(6px)",
628
- WebkitBackdropFilter: "blur(6px)",
629
- display: "flex",
630
- alignItems: "center",
631
- justifyContent: "center"
632
- },
633
- children: /* @__PURE__ */ jsxRuntime.jsx(
634
- DynamicIcon,
635
- {
636
- name: "lucide/external-link",
637
- size: 22,
638
- "aria-hidden": "true"
639
- }
640
- )
641
- }
642
- ),
643
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: { fontSize: 11, fontWeight: 600 }, children: "Instagram" })
644
- ]
666
+ style: { ...USERNAME_BADGE_STYLE, cursor: "pointer" },
667
+ children: badgeContent
645
668
  }
646
- ) : null
669
+ ) : (
670
+ // No username supplied: never fabricate a handle — degrade to a
671
+ // non-interactive "Instagram" chip in the same position.
672
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: USERNAME_BADGE_STYLE, children: badgeContent })
673
+ ),
674
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: VIEWER_TITLE_STYLE, children: item.title })
647
675
  ]
648
676
  }
649
677
  );
@@ -653,7 +681,8 @@ function InstagramFeedGrid({
653
681
  gridClassName,
654
682
  itemClassName,
655
683
  imageClassName,
656
- optixFlowConfig
684
+ optixFlowConfig,
685
+ showLikeBadges = false
657
686
  }) {
658
687
  const { open } = mediaImmersive.useImmersiveFeed();
659
688
  const posterImgProps = React.useMemo(() => {
@@ -666,26 +695,39 @@ function InstagramFeedGrid({
666
695
  "div",
667
696
  {
668
697
  className: cn(
669
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
698
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
670
699
  gridClassName
671
700
  ),
672
701
  children: mediaItems.map((mediaItem) => {
673
702
  const likeCount = mediaItem.meta?.likeCount;
674
- return /* @__PURE__ */ jsxRuntime.jsx(
675
- mediaImmersive.ThumbnailCard,
676
- {
677
- item: mediaItem,
678
- onOpen: open,
679
- size: IG_TILE_WIDTH,
680
- style: IG_TILE_STYLE,
681
- className: itemClassName,
682
- elevated: false,
683
- showDuration: false,
684
- glyphMode: "hover",
685
- posterImgProps,
686
- badgeSlot: typeof likeCount === "number" ? likeBadge(likeCount) : void 0
687
- },
688
- mediaItem.id
703
+ return (
704
+ // The wrapper owns the tile's outer chrome (subtle boundary shadow,
705
+ // matching radius) and the block's own caption overlay — the card's
706
+ // built-in caption is hidden so 1- and 2-line captions can share a
707
+ // fixed-height, top-aligned text box (annotated refinements #2–#4).
708
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: itemClassName, style: TILE_WRAPPER_STYLE, children: [
709
+ /* @__PURE__ */ jsxRuntime.jsx(
710
+ mediaImmersive.ThumbnailCard,
711
+ {
712
+ item: mediaItem,
713
+ onOpen: open,
714
+ size: IG_TILE_WIDTH,
715
+ style: IG_TILE_STYLE,
716
+ elevated: false,
717
+ hideCaption: true,
718
+ hideProgressHint: true,
719
+ showDuration: false,
720
+ glyphMode: "hover",
721
+ posterImgProps,
722
+ badgeSlot: showLikeBadges && typeof likeCount === "number" ? likeBadge(likeCount) : void 0
723
+ }
724
+ ),
725
+ mediaItem.title ? (
726
+ // aria-hidden: the card already announces the post title; this
727
+ // overlay is purely visual. pointer-events pass through to the card.
728
+ /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: CAPTION_SCRIM_STYLE, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: CAPTION_TEXT_STYLE, children: mediaItem.title }) })
729
+ ) : null
730
+ ] }, mediaItem.id)
689
731
  );
690
732
  })
691
733
  }
@@ -698,7 +740,8 @@ function InstagramPostGrid({
698
740
  items,
699
741
  itemsSlot,
700
742
  className,
701
- containerClassName,
743
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
744
+ spacing = "py-12 md:py-32",
702
745
  headerClassName,
703
746
  headingClassName,
704
747
  subheadingClassName,
@@ -706,15 +749,24 @@ function InstagramPostGrid({
706
749
  itemClassName,
707
750
  imageClassName,
708
751
  optixFlowConfig,
752
+ username,
753
+ showLikeBadges = false,
709
754
  background,
710
- spacing,
711
755
  pattern,
712
756
  patternOpacity,
713
757
  patternClassName
714
758
  }) {
715
759
  const mediaItems = React.useMemo(() => {
716
760
  if (itemsSlot || !items) return [];
717
- return items.filter((item) => Boolean(item.image)).map(toMediaItem);
761
+ const withImage = items.filter((item) => Boolean(item.image));
762
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && withImage.length < items.length) {
763
+ const skipped = items.filter((item) => !item.image);
764
+ const skippedIds = skipped.map((item) => item.id).join(", ");
765
+ console.warn(
766
+ `[instagram-post-grid] skipped ${skipped.length} item(s) without a resolvable image: ${skippedIds}`
767
+ );
768
+ }
769
+ return withImage.map(toMediaItem);
718
770
  }, [items, itemsSlot]);
719
771
  if (!itemsSlot && (!items || items.length === 0)) {
720
772
  return null;
@@ -736,7 +788,7 @@ function InstagramPostGrid({
736
788
  "h2",
737
789
  {
738
790
  className: cn(
739
- "text-xl font-medium tracking-tight md:text-2xl lg:text-3xl text-balance",
791
+ "text-xl font-medium tracking-tight md:text-3xl lg:text-5xl text-balance",
740
792
  headingClassName
741
793
  ),
742
794
  children: heading
@@ -746,7 +798,7 @@ function InstagramPostGrid({
746
798
  "p",
747
799
  {
748
800
  className: cn(
749
- "max-w-2xl text-balance text-muted-foreground",
801
+ "max-w-full md:max-w-2xl text-balance opacity-75",
750
802
  subheadingClassName
751
803
  ),
752
804
  children: subheading
@@ -757,7 +809,7 @@ function InstagramPostGrid({
757
809
  "div",
758
810
  {
759
811
  className: cn(
760
- "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4",
812
+ "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
761
813
  gridClassName
762
814
  ),
763
815
  children: itemsSlot
@@ -770,14 +822,16 @@ function InstagramPostGrid({
770
822
  gridClassName,
771
823
  itemClassName,
772
824
  imageClassName,
773
- optixFlowConfig
825
+ optixFlowConfig,
826
+ showLikeBadges
774
827
  }
775
828
  ),
776
829
  /* @__PURE__ */ jsxRuntime.jsx(
777
830
  mediaImmersive.ImmersiveViewer,
778
831
  {
779
832
  ariaLabel: "Instagram post viewer",
780
- renderActions: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(InstagramViewerRail, { item })
833
+ renderActions: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(InstagramViewerRail, { item }),
834
+ renderCaption: (item) => /* @__PURE__ */ jsxRuntime.jsx(InstagramViewerCaption, { item, username })
781
835
  }
782
836
  )
783
837
  ] })
@@ -144,6 +144,22 @@ interface InstagramPostGridProps {
144
144
  * OptixFlow image optimization configuration
145
145
  */
146
146
  optixFlowConfig?: OptixFlowConfig;
147
+ /**
148
+ * Instagram account username (leading `@` tolerated). Renders as a
149
+ * prominent, semi-transparent, clickable `@username` badge in the fullscreen
150
+ * viewer's caption card that opens the account's profile
151
+ * (`https://www.instagram.com/<username>/`) in a new tab. When omitted the
152
+ * badge degrades to a non-clickable "Instagram" chip — a handle is never
153
+ * fabricated.
154
+ */
155
+ username?: string;
156
+ /**
157
+ * Show the like-count pill on grid tiles. Defaults to `false`: clients with
158
+ * low like counts don't want them visible, so the pills stay hidden until
159
+ * per-client dynamic visibility ships. The pill implementation is retained
160
+ * intentionally — flip this prop to bring it back.
161
+ */
162
+ showLikeBadges?: boolean;
147
163
  /** Optional Section ID */
148
164
  sectionId?: string;
149
165
  }
@@ -192,6 +208,6 @@ interface InstagramPostGridProps {
192
208
  * />
193
209
  * ```
194
210
  */
195
- declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, background, spacing, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
211
+ declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, spacing, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, username, showLikeBadges, background, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
196
212
 
197
213
  export { InstagramPostGrid, type InstagramPostGridProps, type InstagramPostItem };
@@ -144,6 +144,22 @@ interface InstagramPostGridProps {
144
144
  * OptixFlow image optimization configuration
145
145
  */
146
146
  optixFlowConfig?: OptixFlowConfig;
147
+ /**
148
+ * Instagram account username (leading `@` tolerated). Renders as a
149
+ * prominent, semi-transparent, clickable `@username` badge in the fullscreen
150
+ * viewer's caption card that opens the account's profile
151
+ * (`https://www.instagram.com/<username>/`) in a new tab. When omitted the
152
+ * badge degrades to a non-clickable "Instagram" chip — a handle is never
153
+ * fabricated.
154
+ */
155
+ username?: string;
156
+ /**
157
+ * Show the like-count pill on grid tiles. Defaults to `false`: clients with
158
+ * low like counts don't want them visible, so the pills stay hidden until
159
+ * per-client dynamic visibility ships. The pill implementation is retained
160
+ * intentionally — flip this prop to bring it back.
161
+ */
162
+ showLikeBadges?: boolean;
147
163
  /** Optional Section ID */
148
164
  sectionId?: string;
149
165
  }
@@ -192,6 +208,6 @@ interface InstagramPostGridProps {
192
208
  * />
193
209
  * ```
194
210
  */
195
- declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, background, spacing, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
211
+ declare function InstagramPostGrid({ sectionId, heading, subheading, items, itemsSlot, className, containerClassName, spacing, headerClassName, headingClassName, subheadingClassName, gridClassName, itemClassName, imageClassName, optixFlowConfig, username, showLikeBadges, background, pattern, patternOpacity, patternClassName, }: InstagramPostGridProps): React.JSX.Element | null;
196
212
 
197
213
  export { InstagramPostGrid, type InstagramPostGridProps, type InstagramPostItem };