@rmdes/indiekit-endpoint-activitypub 2.6.0 → 2.6.1
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.
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blurhash placeholder backgrounds for gallery images.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the average (DC) color from a blurhash string and applies it
|
|
5
|
+
* as a background-color on images with a data-blurhash attribute.
|
|
6
|
+
* This provides a meaningful colored placeholder while images load.
|
|
7
|
+
*
|
|
8
|
+
* The DC component is encoded in the first 4 characters of the blurhash
|
|
9
|
+
* after the size byte, as a base-83 integer representing an sRGB color.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const BASE83_CHARS =
|
|
13
|
+
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~";
|
|
14
|
+
|
|
15
|
+
function decode83(str) {
|
|
16
|
+
let value = 0;
|
|
17
|
+
for (const c of str) {
|
|
18
|
+
const digit = BASE83_CHARS.indexOf(c);
|
|
19
|
+
if (digit === -1) return 0;
|
|
20
|
+
value = value * 83 + digit;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function decodeDC(value) {
|
|
26
|
+
return {
|
|
27
|
+
r: (value >> 16) & 255,
|
|
28
|
+
g: (value >> 8) & 255,
|
|
29
|
+
b: value & 255,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function blurhashToColor(hash) {
|
|
34
|
+
if (!hash || hash.length < 6) return null;
|
|
35
|
+
const dcValue = decode83(hash.slice(1, 5));
|
|
36
|
+
const { r, g, b } = decodeDC(dcValue);
|
|
37
|
+
return `rgb(${r},${g},${b})`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
41
|
+
for (const img of document.querySelectorAll("img[data-blurhash]")) {
|
|
42
|
+
const color = blurhashToColor(img.dataset.blurhash);
|
|
43
|
+
if (color) {
|
|
44
|
+
img.style.backgroundColor = color;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Handle dynamically loaded images (infinite scroll)
|
|
49
|
+
const observer = new MutationObserver((mutations) => {
|
|
50
|
+
for (const mutation of mutations) {
|
|
51
|
+
for (const node of mutation.addedNodes) {
|
|
52
|
+
if (node.nodeType !== 1) continue;
|
|
53
|
+
const imgs = node.querySelectorAll
|
|
54
|
+
? node.querySelectorAll("img[data-blurhash]")
|
|
55
|
+
: [];
|
|
56
|
+
for (const img of imgs) {
|
|
57
|
+
const color = blurhashToColor(img.dataset.blurhash);
|
|
58
|
+
if (color) {
|
|
59
|
+
img.style.backgroundColor = color;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
66
|
+
});
|
package/assets/reader-links.css
CHANGED
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
.ap-link-preview {
|
|
16
16
|
display: flex;
|
|
17
17
|
overflow: hidden;
|
|
18
|
-
border-radius:
|
|
18
|
+
border-radius: 8px;
|
|
19
19
|
border: 1px solid var(--color-neutral-lighter);
|
|
20
20
|
background-color: var(--color-offset);
|
|
21
21
|
text-decoration: none;
|
|
22
22
|
color: inherit;
|
|
23
|
-
transition: border-color 0.
|
|
23
|
+
transition: border-color 0.15s ease;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.ap-link-preview:hover {
|
|
27
|
-
border-color: var(--color-
|
|
27
|
+
border-color: var(--color-outline-variant);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/* Text content area (left side) */
|
package/assets/reader.css
CHANGED
|
@@ -150,10 +150,11 @@
|
|
|
150
150
|
.ap-card {
|
|
151
151
|
background: var(--color-offset);
|
|
152
152
|
border: var(--border-width-thin) solid var(--color-outline);
|
|
153
|
-
border-left:
|
|
154
|
-
border-radius:
|
|
153
|
+
border-left: 3px solid var(--color-outline);
|
|
154
|
+
border-radius: 8px;
|
|
155
155
|
overflow: hidden;
|
|
156
156
|
padding: var(--space-m);
|
|
157
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
157
158
|
transition:
|
|
158
159
|
box-shadow 0.2s ease,
|
|
159
160
|
border-color 0.2s ease;
|
|
@@ -162,6 +163,7 @@
|
|
|
162
163
|
.ap-card:hover {
|
|
163
164
|
border-color: var(--color-outline-variant);
|
|
164
165
|
border-left-color: var(--color-outline-variant);
|
|
166
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/* ==========================================================================
|
|
@@ -263,9 +265,9 @@
|
|
|
263
265
|
border: var(--border-width-thin) solid var(--color-outline);
|
|
264
266
|
border-radius: 50%;
|
|
265
267
|
flex-shrink: 0;
|
|
266
|
-
height:
|
|
268
|
+
height: 44px;
|
|
267
269
|
object-fit: cover;
|
|
268
|
-
width:
|
|
270
|
+
width: 44px;
|
|
269
271
|
}
|
|
270
272
|
|
|
271
273
|
.ap-card__avatar--default {
|
|
@@ -282,10 +284,12 @@
|
|
|
282
284
|
display: flex;
|
|
283
285
|
flex-direction: column;
|
|
284
286
|
flex: 1;
|
|
287
|
+
gap: 1px;
|
|
285
288
|
min-width: 0;
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
.ap-card__author-name {
|
|
292
|
+
font-size: 0.95em;
|
|
289
293
|
font-weight: 600;
|
|
290
294
|
overflow: hidden;
|
|
291
295
|
text-overflow: ellipsis;
|
|
@@ -327,7 +331,7 @@
|
|
|
327
331
|
.ap-card__timestamp {
|
|
328
332
|
color: var(--color-on-offset);
|
|
329
333
|
flex-shrink: 0;
|
|
330
|
-
font-size: var(--font-size-
|
|
334
|
+
font-size: var(--font-size-s);
|
|
331
335
|
}
|
|
332
336
|
|
|
333
337
|
.ap-card__edited {
|
|
@@ -374,7 +378,7 @@
|
|
|
374
378
|
|
|
375
379
|
.ap-card__content {
|
|
376
380
|
color: var(--color-on-background);
|
|
377
|
-
line-height:
|
|
381
|
+
line-height: calc(4 / 3 * 1em);
|
|
378
382
|
margin-bottom: var(--space-s);
|
|
379
383
|
overflow-wrap: break-word;
|
|
380
384
|
word-break: break-word;
|
|
@@ -504,7 +508,7 @@
|
|
|
504
508
|
========================================================================== */
|
|
505
509
|
|
|
506
510
|
.ap-card__gallery {
|
|
507
|
-
border-radius:
|
|
511
|
+
border-radius: 6px;
|
|
508
512
|
display: grid;
|
|
509
513
|
gap: 2px;
|
|
510
514
|
margin-bottom: var(--space-s);
|
|
@@ -524,9 +528,14 @@
|
|
|
524
528
|
.ap-card__gallery img {
|
|
525
529
|
background: var(--color-offset-variant);
|
|
526
530
|
display: block;
|
|
527
|
-
height:
|
|
531
|
+
height: 220px;
|
|
528
532
|
object-fit: cover;
|
|
529
533
|
width: 100%;
|
|
534
|
+
transition: filter 0.2s ease;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.ap-card__gallery-link:hover img {
|
|
538
|
+
filter: brightness(0.92);
|
|
530
539
|
}
|
|
531
540
|
|
|
532
541
|
.ap-card__gallery-link--more::after {
|
|
@@ -557,7 +566,7 @@
|
|
|
557
566
|
|
|
558
567
|
.ap-card__gallery--1 img {
|
|
559
568
|
height: auto;
|
|
560
|
-
max-height:
|
|
569
|
+
max-height: 500px;
|
|
561
570
|
}
|
|
562
571
|
|
|
563
572
|
/* 2 photos — side by side */
|
|
@@ -761,60 +770,86 @@
|
|
|
761
770
|
border-top: var(--border-width-thin) solid var(--color-outline);
|
|
762
771
|
display: flex;
|
|
763
772
|
flex-wrap: wrap;
|
|
764
|
-
gap:
|
|
773
|
+
gap: 2px;
|
|
765
774
|
padding-top: var(--space-s);
|
|
766
775
|
}
|
|
767
776
|
|
|
768
777
|
.ap-card__action {
|
|
769
778
|
align-items: center;
|
|
770
779
|
background: transparent;
|
|
771
|
-
border:
|
|
772
|
-
border-radius:
|
|
780
|
+
border: 0;
|
|
781
|
+
border-radius: 6px;
|
|
773
782
|
color: var(--color-on-offset);
|
|
774
783
|
cursor: pointer;
|
|
775
784
|
display: inline-flex;
|
|
776
785
|
font-size: var(--font-size-s);
|
|
777
|
-
gap:
|
|
778
|
-
|
|
786
|
+
gap: 0.3em;
|
|
787
|
+
min-height: 36px;
|
|
788
|
+
padding: 0.25em 0.6em;
|
|
779
789
|
text-decoration: none;
|
|
780
|
-
transition:
|
|
790
|
+
transition:
|
|
791
|
+
background-color 0.15s ease,
|
|
792
|
+
color 0.15s ease;
|
|
781
793
|
}
|
|
782
794
|
|
|
783
795
|
.ap-card__action:hover {
|
|
784
796
|
background: var(--color-offset-variant);
|
|
785
|
-
border-color: var(--color-outline-variant);
|
|
786
797
|
color: var(--color-on-background);
|
|
787
798
|
}
|
|
788
799
|
|
|
789
|
-
/*
|
|
800
|
+
/* Color-coded hover states per action type */
|
|
801
|
+
.ap-card__action--reply:hover {
|
|
802
|
+
background: color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
803
|
+
color: var(--color-primary);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
.ap-card__action--boost:hover {
|
|
807
|
+
background: color-mix(in srgb, var(--color-green50) 12%, transparent);
|
|
808
|
+
color: var(--color-green50);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.ap-card__action--like:hover {
|
|
812
|
+
background: color-mix(in srgb, var(--color-red45) 12%, transparent);
|
|
813
|
+
color: var(--color-red45);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.ap-card__action--link:hover {
|
|
817
|
+
background: var(--color-offset-variant);
|
|
818
|
+
color: var(--color-on-background);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
.ap-card__action--save:hover {
|
|
822
|
+
background: color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
823
|
+
color: var(--color-primary);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/* Active interaction states */
|
|
790
827
|
.ap-card__action--like.ap-card__action--active {
|
|
791
|
-
background: var(--color-
|
|
792
|
-
border-color: var(--color-red45);
|
|
828
|
+
background: color-mix(in srgb, var(--color-red45) 12%, transparent);
|
|
793
829
|
color: var(--color-red45);
|
|
794
830
|
}
|
|
795
831
|
|
|
796
832
|
.ap-card__action--boost.ap-card__action--active {
|
|
797
|
-
background: var(--color-
|
|
798
|
-
border-color: var(--color-green50);
|
|
833
|
+
background: color-mix(in srgb, var(--color-green50) 12%, transparent);
|
|
799
834
|
color: var(--color-green50);
|
|
800
835
|
}
|
|
801
836
|
|
|
802
837
|
.ap-card__action--save.ap-card__action--active {
|
|
803
|
-
background:
|
|
804
|
-
|
|
805
|
-
color: #4a9eff;
|
|
838
|
+
background: color-mix(in srgb, var(--color-primary) 12%, transparent);
|
|
839
|
+
color: var(--color-primary);
|
|
806
840
|
}
|
|
807
841
|
|
|
808
842
|
.ap-card__action:disabled {
|
|
809
843
|
cursor: wait;
|
|
810
|
-
opacity: 0.
|
|
844
|
+
opacity: 0.5;
|
|
811
845
|
}
|
|
812
846
|
|
|
813
847
|
/* Interaction counts */
|
|
814
848
|
.ap-card__count {
|
|
815
849
|
font-size: var(--font-size-xs);
|
|
816
|
-
color:
|
|
817
|
-
|
|
850
|
+
color: inherit;
|
|
851
|
+
opacity: 0.7;
|
|
852
|
+
margin-left: 0.1em;
|
|
818
853
|
font-variant-numeric: tabular-nums;
|
|
819
854
|
}
|
|
820
855
|
|
|
@@ -2536,9 +2571,14 @@
|
|
|
2536
2571
|
|
|
2537
2572
|
.ap-quote-embed {
|
|
2538
2573
|
border: var(--border-width-thin) solid var(--color-outline);
|
|
2539
|
-
border-radius:
|
|
2574
|
+
border-radius: 8px;
|
|
2540
2575
|
margin-top: var(--space-s);
|
|
2541
2576
|
overflow: hidden;
|
|
2577
|
+
transition: border-color 0.15s ease;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
.ap-quote-embed:hover {
|
|
2581
|
+
border-color: var(--color-outline-variant);
|
|
2542
2582
|
}
|
|
2543
2583
|
|
|
2544
2584
|
.ap-quote-embed--pending {
|
|
@@ -2553,7 +2593,7 @@
|
|
|
2553
2593
|
}
|
|
2554
2594
|
|
|
2555
2595
|
.ap-quote-embed__link:hover {
|
|
2556
|
-
background: var(--color-offset);
|
|
2596
|
+
background: color-mix(in srgb, var(--color-offset) 50%, transparent);
|
|
2557
2597
|
}
|
|
2558
2598
|
|
|
2559
2599
|
.ap-quote-embed__author {
|
|
@@ -2618,8 +2658,8 @@
|
|
|
2618
2658
|
.ap-quote-embed__content {
|
|
2619
2659
|
color: var(--color-on-background);
|
|
2620
2660
|
font-size: var(--font-size-s);
|
|
2621
|
-
line-height:
|
|
2622
|
-
max-height: calc(1.
|
|
2661
|
+
line-height: calc(4 / 3 * 1em);
|
|
2662
|
+
max-height: calc(1.333em * 6);
|
|
2623
2663
|
overflow: hidden;
|
|
2624
2664
|
}
|
|
2625
2665
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.1",
|
|
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",
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
{# AP link interception for internal navigation #}
|
|
21
21
|
<script defer src="/assets/@rmdes-indiekit-endpoint-activitypub/reader-links.js"></script>
|
|
22
|
+
{# Blurhash placeholder backgrounds for gallery images #}
|
|
23
|
+
<script defer src="/assets/@rmdes-indiekit-endpoint-activitypub/reader-blurhash.js"></script>
|
|
22
24
|
|
|
23
25
|
{% if readerParent %}
|
|
24
26
|
<nav class="ap-breadcrumb" aria-label="Breadcrumb">
|
|
@@ -10,10 +10,18 @@
|
|
|
10
10
|
{# Support both old string format and new object format #}
|
|
11
11
|
{% set photoSrc = photo.url if photo.url else photo %}
|
|
12
12
|
{% set photoAlt = photo.alt if photo.alt else "" %}
|
|
13
|
+
{% set photoBlurhash = photo.blurhash if photo.blurhash else "" %}
|
|
14
|
+
{# Focus-point cropping: convert -1..1 range to CSS object-position percentages #}
|
|
15
|
+
{% set focusStyle = "" %}
|
|
16
|
+
{% if photo.focus and photo.focus.x != null and photo.focus.y != null %}
|
|
17
|
+
{% set fpX = ((photo.focus.x + 1) / 2 * 100) %}
|
|
18
|
+
{% set fpY = ((1 - (photo.focus.y + 1) / 2) * 100) %}
|
|
19
|
+
{% set focusStyle = "object-position:" + fpX + "% " + fpY + "%;" %}
|
|
20
|
+
{% endif %}
|
|
13
21
|
{% if loop.index0 < 4 %}
|
|
14
22
|
<div class="ap-card__gallery-item" x-data="{ showAlt: false }">
|
|
15
23
|
<button type="button" @click="idx = {{ loop.index0 }}; lightbox = true" class="ap-card__gallery-link{% if loop.index0 == 3 and extraCount > 0 %} ap-card__gallery-link--more{% endif %}">
|
|
16
|
-
<img src="{{ photoSrc }}" alt="{{ photoAlt }}" loading="lazy">
|
|
24
|
+
<img src="{{ photoSrc }}" alt="{{ photoAlt }}" loading="lazy"{% if focusStyle %} style="{{ focusStyle }}"{% endif %}{% if photoBlurhash %} data-blurhash="{{ photoBlurhash }}"{% endif %}>
|
|
17
25
|
{% if loop.index0 == 3 and extraCount > 0 %}
|
|
18
26
|
<span class="ap-card__gallery-more">+{{ extraCount }}</span>
|
|
19
27
|
{% endif %}
|
|
@@ -26,8 +26,14 @@
|
|
|
26
26
|
{% endif %}
|
|
27
27
|
{% if item.quote.photo and item.quote.photo.length > 0 %}
|
|
28
28
|
{% set qPhoto = item.quote.photo[0] %}
|
|
29
|
+
{% set qFocusStyle = "" %}
|
|
30
|
+
{% if qPhoto.focus and qPhoto.focus.x != null and qPhoto.focus.y != null %}
|
|
31
|
+
{% set qFpX = ((qPhoto.focus.x + 1) / 2 * 100) %}
|
|
32
|
+
{% set qFpY = ((1 - (qPhoto.focus.y + 1) / 2) * 100) %}
|
|
33
|
+
{% set qFocusStyle = "object-position:" + qFpX + "% " + qFpY + "%;" %}
|
|
34
|
+
{% endif %}
|
|
29
35
|
<div class="ap-quote-embed__media">
|
|
30
|
-
<img src="{{ qPhoto.url if qPhoto.url else qPhoto }}" alt="{{ qPhoto.alt if qPhoto.alt else '' }}" loading="lazy" class="ap-quote-embed__photo">
|
|
36
|
+
<img src="{{ qPhoto.url if qPhoto.url else qPhoto }}" alt="{{ qPhoto.alt if qPhoto.alt else '' }}" loading="lazy" class="ap-quote-embed__photo"{% if qFocusStyle %} style="{{ qFocusStyle }}"{% endif %}>
|
|
31
37
|
</div>
|
|
32
38
|
{% endif %}
|
|
33
39
|
</a>
|