@rmdes/indiekit-endpoint-activitypub 2.7.1 → 2.8.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/assets/reader.css CHANGED
@@ -528,12 +528,18 @@
528
528
  .ap-card__gallery img {
529
529
  background: var(--color-offset-variant);
530
530
  display: block;
531
- height: 220px;
531
+ height: 280px;
532
532
  object-fit: cover;
533
533
  width: 100%;
534
534
  transition: filter 0.2s ease;
535
535
  }
536
536
 
537
+ @media (max-width: 480px) {
538
+ .ap-card__gallery img {
539
+ height: 180px;
540
+ }
541
+ }
542
+
537
543
  .ap-card__gallery-link:hover img {
538
544
  filter: brightness(0.92);
539
545
  }
@@ -668,6 +674,83 @@
668
674
  transform: translateX(-50%);
669
675
  }
670
676
 
677
+ /* ==========================================================================
678
+ Link Preview Card
679
+ ========================================================================== */
680
+
681
+ .ap-link-previews {
682
+ margin-bottom: var(--space-s);
683
+ }
684
+
685
+ .ap-link-preview {
686
+ display: flex;
687
+ border: var(--border-width-thin) solid var(--color-outline);
688
+ border-radius: var(--border-radius-small);
689
+ overflow: hidden;
690
+ text-decoration: none;
691
+ color: inherit;
692
+ transition: border-color 0.2s ease;
693
+ }
694
+
695
+ .ap-link-preview:hover {
696
+ border-color: var(--color-primary);
697
+ }
698
+
699
+ .ap-link-preview__text {
700
+ flex: 1;
701
+ min-width: 0;
702
+ padding: var(--space-s) var(--space-m);
703
+ display: flex;
704
+ flex-direction: column;
705
+ justify-content: center;
706
+ gap: 0.2em;
707
+ }
708
+
709
+ .ap-link-preview__title {
710
+ font-weight: var(--font-weight-bold);
711
+ font-size: var(--font-size-s);
712
+ margin: 0;
713
+ overflow: hidden;
714
+ text-overflow: ellipsis;
715
+ white-space: nowrap;
716
+ }
717
+
718
+ .ap-link-preview__desc {
719
+ font-size: var(--font-size-s);
720
+ color: var(--color-on-offset);
721
+ margin: 0;
722
+ display: -webkit-box;
723
+ -webkit-line-clamp: 2;
724
+ -webkit-box-orient: vertical;
725
+ overflow: hidden;
726
+ }
727
+
728
+ .ap-link-preview__domain {
729
+ font-size: var(--font-size-xs);
730
+ color: var(--color-on-offset);
731
+ margin: 0;
732
+ display: flex;
733
+ align-items: center;
734
+ gap: 0.3em;
735
+ }
736
+
737
+ .ap-link-preview__favicon {
738
+ width: 14px;
739
+ height: 14px;
740
+ }
741
+
742
+ .ap-link-preview__image {
743
+ flex-shrink: 0;
744
+ width: 120px;
745
+ }
746
+
747
+ .ap-link-preview__image img {
748
+ display: block;
749
+ width: 100%;
750
+ height: 100%;
751
+ object-fit: cover;
752
+ }
753
+
671
754
  /* ==========================================================================
672
755
  Video Embed
673
756
  ========================================================================== */
package/lib/jf2-to-as2.js CHANGED
@@ -20,6 +20,22 @@ import {
20
20
  Video,
21
21
  } from "@fedify/fedify/vocab";
22
22
 
23
+ // ---------------------------------------------------------------------------
24
+ // Content helpers
25
+ // ---------------------------------------------------------------------------
26
+
27
+ /**
28
+ * Convert bare URLs in HTML content to clickable links.
29
+ * Skips URLs already inside href attributes or anchor tag text.
30
+ */
31
+ function linkifyUrls(html) {
32
+ if (!html) return html;
33
+ return html.replace(
34
+ /(?<![=">])(https?:\/\/[^\s<"]+)/g,
35
+ '<a href="$1">$1</a>',
36
+ );
37
+ }
38
+
23
39
  // ---------------------------------------------------------------------------
24
40
  // Plain JSON-LD (content negotiation on individual post URLs)
25
41
  // ---------------------------------------------------------------------------
@@ -68,7 +84,7 @@ export function jf2ToActivityStreams(properties, actorUrl, publicationUrl) {
68
84
 
69
85
  if (postType === "bookmark") {
70
86
  const bookmarkUrl = properties["bookmark-of"];
71
- const commentary = properties.content?.html || properties.content || "";
87
+ const commentary = linkifyUrls(properties.content?.html || properties.content || "");
72
88
  object.content = commentary
73
89
  ? `${commentary}<br><br>\u{1F516} <a href="${bookmarkUrl}">${bookmarkUrl}</a>`
74
90
  : `\u{1F516} <a href="${bookmarkUrl}">${bookmarkUrl}</a>`;
@@ -80,7 +96,7 @@ export function jf2ToActivityStreams(properties, actorUrl, publicationUrl) {
80
96
  },
81
97
  ];
82
98
  } else {
83
- object.content = properties.content?.html || properties.content || "";
99
+ object.content = linkifyUrls(properties.content?.html || properties.content || "");
84
100
  }
85
101
 
86
102
  // Append permalink to content so fediverse clients show a clickable link
@@ -193,12 +209,12 @@ export function jf2ToAS2Activity(properties, actorUrl, publicationUrl, options =
193
209
  // Content
194
210
  if (postType === "bookmark") {
195
211
  const bookmarkUrl = properties["bookmark-of"];
196
- const commentary = properties.content?.html || properties.content || "";
212
+ const commentary = linkifyUrls(properties.content?.html || properties.content || "");
197
213
  noteOptions.content = commentary
198
214
  ? `${commentary}<br><br>\u{1F516} <a href="${bookmarkUrl}">${bookmarkUrl}</a>`
199
215
  : `\u{1F516} <a href="${bookmarkUrl}">${bookmarkUrl}</a>`;
200
216
  } else {
201
- noteOptions.content = properties.content?.html || properties.content || "";
217
+ noteOptions.content = linkifyUrls(properties.content?.html || properties.content || "");
202
218
  }
203
219
 
204
220
  // Append permalink to content so fediverse clients show a clickable link
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "2.7.1",
3
+ "version": "2.8.0",
4
4
  "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
5
5
  "keywords": [
6
6
  "indiekit",
@@ -5,7 +5,7 @@
5
5
  {% set displayCount = item.photo.length if item.photo.length < 4 else 4 %}
6
6
  {% set extraCount = item.photo.length - 4 %}
7
7
  {% set totalPhotos = item.photo.length %}
8
- <div x-data="{ lightbox: false, idx: 0 }" class="ap-card__gallery ap-card__gallery--{{ displayCount }}">
8
+ <div x-data="{ lightbox: false, idx: 0, touchX: 0 }" class="ap-card__gallery ap-card__gallery--{{ displayCount }}">
9
9
  {% for photo in item.photo %}
10
10
  {# Support both old string format and new object format #}
11
11
  {% set photoSrc = photo.url if photo.url else photo %}
@@ -36,7 +36,7 @@
36
36
 
37
37
  {# Lightbox modal — teleported to body to prevent overflow clipping #}
38
38
  <template x-teleport="body">
39
- <div x-show="lightbox" x-cloak @keydown.escape.window="lightbox = false" @click.self="lightbox = false" class="ap-lightbox" role="dialog" aria-modal="true">
39
+ <div x-show="lightbox" x-cloak @keydown.escape.window="lightbox = false" @click.self="lightbox = false" @touchstart="touchX = $event.changedTouches[0].clientX" @touchend="let dx = $event.changedTouches[0].clientX - touchX; if (dx < -50) idx = (idx + 1) % {{ totalPhotos }}; else if (dx > 50) idx = (idx - 1 + {{ totalPhotos }}) % {{ totalPhotos }}" class="ap-lightbox" role="dialog" aria-modal="true">
40
40
  <button type="button" @click="lightbox = false" class="ap-lightbox__close" aria-label="Close">&times;</button>
41
41
  {% if totalPhotos > 1 %}
42
42
  <button type="button" @click="idx = (idx - 1 + {{ totalPhotos }}) % {{ totalPhotos }}" class="ap-lightbox__prev" aria-label="Previous image">&lsaquo;</button>