@rmdes/indiekit-endpoint-activitypub 1.1.4 → 1.1.6

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
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ActivityPub Reader Styles
3
3
  * Card-based layout inspired by Phanpy/Elk
4
- * Uses Indiekit CSS custom properties
4
+ * Uses Indiekit CSS custom properties for automatic dark mode support
5
5
  */
6
6
 
7
7
  /* ==========================================================================
@@ -9,7 +9,7 @@
9
9
  ========================================================================== */
10
10
 
11
11
  .ap-tabs {
12
- border-bottom: 1px solid var(--color-offset);
12
+ border-bottom: var(--border-width-thin) solid var(--color-outline);
13
13
  display: flex;
14
14
  gap: var(--space-xs);
15
15
  margin-bottom: var(--space-m);
@@ -18,9 +18,9 @@
18
18
  }
19
19
 
20
20
  .ap-tab {
21
- border-bottom: 2px solid transparent;
22
- color: var(--color-text-muted);
23
- font-size: var(--font-size-body);
21
+ border-bottom: var(--border-width-thick) solid transparent;
22
+ color: var(--color-on-offset);
23
+ font-size: var(--font-size-m);
24
24
  padding: var(--space-s) var(--space-m);
25
25
  text-decoration: none;
26
26
  transition:
@@ -30,7 +30,7 @@
30
30
  }
31
31
 
32
32
  .ap-tab:hover {
33
- color: var(--color-text);
33
+ color: var(--color-on-background);
34
34
  }
35
35
 
36
36
  .ap-tab--active {
@@ -50,13 +50,14 @@
50
50
  }
51
51
 
52
52
  /* ==========================================================================
53
- Item Card
53
+ Item Card — Base
54
54
  ========================================================================== */
55
55
 
56
56
  .ap-card {
57
- background: var(--color-background);
58
- border: 1px solid var(--color-offset);
59
- border-radius: var(--border-radius);
57
+ background: var(--color-offset);
58
+ border: var(--border-width-thin) solid var(--color-outline);
59
+ border-left: var(--border-width-thickest) solid var(--color-outline);
60
+ border-radius: var(--border-radius-small);
60
61
  overflow: hidden;
61
62
  padding: var(--space-m);
62
63
  transition:
@@ -65,32 +66,79 @@
65
66
  }
66
67
 
67
68
  .ap-card:hover {
68
- border-color: var(--color-offset-active);
69
+ border-color: var(--color-outline-variant);
70
+ border-left-color: var(--color-outline-variant);
71
+ }
72
+
73
+ /* ==========================================================================
74
+ Item Card — Post Type Differentiation
75
+ ========================================================================== */
76
+
77
+ /* Notes: default purple-ish accent (the most common type) */
78
+ .ap-card--note {
79
+ border-left-color: var(--color-purple45);
80
+ }
81
+
82
+ .ap-card--note:hover {
83
+ border-left-color: var(--color-purple45);
84
+ }
85
+
86
+ /* Articles: green accent (long-form content stands out) */
87
+ .ap-card--article {
88
+ border-left-color: var(--color-green50);
69
89
  }
70
90
 
71
- /* Boost header */
91
+ .ap-card--article:hover {
92
+ border-left-color: var(--color-green50);
93
+ }
94
+
95
+ /* Boosts: yellow accent (shared content) */
96
+ .ap-card--boost {
97
+ border-left-color: var(--color-yellow50);
98
+ }
99
+
100
+ .ap-card--boost:hover {
101
+ border-left-color: var(--color-yellow50);
102
+ }
103
+
104
+ /* Replies: blue accent (via primary color) */
105
+ .ap-card--reply {
106
+ border-left-color: var(--color-primary);
107
+ }
108
+
109
+ .ap-card--reply:hover {
110
+ border-left-color: var(--color-primary);
111
+ }
112
+
113
+ /* ==========================================================================
114
+ Boost Header
115
+ ========================================================================== */
116
+
72
117
  .ap-card__boost {
73
- color: var(--color-text-muted);
74
- font-size: var(--font-size-small);
118
+ color: var(--color-on-offset);
119
+ font-size: var(--font-size-s);
75
120
  margin-bottom: var(--space-s);
76
121
  padding-bottom: var(--space-xs);
77
122
  }
78
123
 
79
124
  .ap-card__boost a {
80
- color: var(--color-text-muted);
125
+ color: var(--color-on-offset);
81
126
  font-weight: 600;
82
127
  text-decoration: none;
83
128
  }
84
129
 
85
130
  .ap-card__boost a:hover {
86
- color: var(--color-text);
131
+ color: var(--color-on-background);
87
132
  text-decoration: underline;
88
133
  }
89
134
 
90
- /* Reply context */
135
+ /* ==========================================================================
136
+ Reply Context
137
+ ========================================================================== */
138
+
91
139
  .ap-card__reply-to {
92
- color: var(--color-text-muted);
93
- font-size: var(--font-size-small);
140
+ color: var(--color-on-offset);
141
+ font-size: var(--font-size-s);
94
142
  margin-bottom: var(--space-s);
95
143
  overflow: hidden;
96
144
  text-overflow: ellipsis;
@@ -106,7 +154,10 @@
106
154
  text-decoration: underline;
107
155
  }
108
156
 
109
- /* Author header */
157
+ /* ==========================================================================
158
+ Author Header
159
+ ========================================================================== */
160
+
110
161
  .ap-card__author {
111
162
  align-items: center;
112
163
  display: flex;
@@ -115,7 +166,7 @@
115
166
  }
116
167
 
117
168
  .ap-card__avatar {
118
- border: 1px solid var(--color-offset);
169
+ border: var(--border-width-thin) solid var(--color-outline);
119
170
  border-radius: 50%;
120
171
  flex-shrink: 0;
121
172
  height: 40px;
@@ -125,8 +176,8 @@
125
176
 
126
177
  .ap-card__avatar--default {
127
178
  align-items: center;
128
- background: var(--color-offset);
129
- color: var(--color-text-muted);
179
+ background: var(--color-offset-variant);
180
+ color: var(--color-on-offset);
130
181
  display: inline-flex;
131
182
  font-size: 1.1em;
132
183
  font-weight: 600;
@@ -157,24 +208,27 @@
157
208
  }
158
209
 
159
210
  .ap-card__author-handle {
160
- color: var(--color-text-muted);
161
- font-size: var(--font-size-small);
211
+ color: var(--color-on-offset);
212
+ font-size: var(--font-size-s);
162
213
  overflow: hidden;
163
214
  text-overflow: ellipsis;
164
215
  white-space: nowrap;
165
216
  }
166
217
 
167
218
  .ap-card__timestamp {
168
- color: var(--color-text-muted);
219
+ color: var(--color-on-offset);
169
220
  flex-shrink: 0;
170
- font-size: var(--font-size-small);
221
+ font-size: var(--font-size-xs);
171
222
  }
172
223
 
173
- /* Post title (articles) */
224
+ /* ==========================================================================
225
+ Post Title (Articles)
226
+ ========================================================================== */
227
+
174
228
  .ap-card__title {
175
- font-size: var(--font-size-heading-4);
229
+ font-size: var(--font-size-l);
176
230
  font-weight: 600;
177
- line-height: 1.3;
231
+ line-height: var(--line-height-tight);
178
232
  margin-bottom: var(--space-s);
179
233
  }
180
234
 
@@ -187,10 +241,13 @@
187
241
  text-decoration: underline;
188
242
  }
189
243
 
190
- /* Content */
244
+ /* ==========================================================================
245
+ Content
246
+ ========================================================================== */
247
+
191
248
  .ap-card__content {
192
- color: var(--color-text);
193
- line-height: 1.6;
249
+ color: var(--color-on-background);
250
+ line-height: var(--line-height-prose);
194
251
  margin-bottom: var(--space-s);
195
252
  overflow-wrap: break-word;
196
253
  word-break: break-word;
@@ -209,20 +266,20 @@
209
266
  }
210
267
 
211
268
  .ap-card__content blockquote {
212
- border-left: 3px solid var(--color-offset);
269
+ border-left: var(--border-width-thickest) solid var(--color-outline);
213
270
  margin: var(--space-s) 0;
214
271
  padding-left: var(--space-m);
215
272
  }
216
273
 
217
274
  .ap-card__content pre {
218
- background: var(--color-offset);
219
- border-radius: var(--border-radius);
275
+ background: var(--color-offset-variant);
276
+ border-radius: var(--border-radius-small);
220
277
  overflow-x: auto;
221
278
  padding: var(--space-s);
222
279
  }
223
280
 
224
281
  .ap-card__content code {
225
- background: var(--color-offset);
282
+ background: var(--color-offset-variant);
226
283
  border-radius: 3px;
227
284
  font-size: 0.9em;
228
285
  padding: 1px 4px;
@@ -234,24 +291,61 @@
234
291
  }
235
292
 
236
293
  .ap-card__content img {
237
- border-radius: var(--border-radius);
294
+ border-radius: var(--border-radius-small);
238
295
  height: auto;
239
296
  max-width: 100%;
240
297
  }
241
298
 
242
- /* Content warning */
299
+ /* @mentions styled as subtle pills to distinguish from prose */
300
+ .ap-card__content .h-card,
301
+ .ap-card__content a.u-url.mention {
302
+ color: var(--color-on-offset);
303
+ font-size: var(--font-size-s);
304
+ text-decoration: none;
305
+ }
306
+
307
+ .ap-card__content a.u-url.mention:hover {
308
+ color: var(--color-primary);
309
+ text-decoration: underline;
310
+ }
311
+
312
+ /* Hashtag mentions — subtle tag styling */
313
+ .ap-card__content a.mention.hashtag {
314
+ color: var(--color-on-offset);
315
+ font-size: var(--font-size-s);
316
+ text-decoration: none;
317
+ }
318
+
319
+ .ap-card__content a.mention.hashtag:hover {
320
+ color: var(--color-primary);
321
+ text-decoration: underline;
322
+ }
323
+
324
+ /* Mastodon's invisible/ellipsis spans for long URLs */
325
+ .ap-card__content .invisible {
326
+ display: none;
327
+ }
328
+
329
+ .ap-card__content .ellipsis::after {
330
+ content: "…";
331
+ }
332
+
333
+ /* ==========================================================================
334
+ Content Warning
335
+ ========================================================================== */
336
+
243
337
  .ap-card__cw {
244
338
  margin-bottom: var(--space-s);
245
339
  }
246
340
 
247
341
  .ap-card__cw-toggle {
248
- background: var(--color-offset);
249
- border: 1px solid var(--color-offset-active);
250
- border-radius: var(--border-radius);
251
- color: var(--color-text);
342
+ background: var(--color-offset-variant);
343
+ border: var(--border-width-thin) solid var(--color-outline);
344
+ border-radius: var(--border-radius-small);
345
+ color: var(--color-on-background);
252
346
  cursor: pointer;
253
347
  display: block;
254
- font-size: var(--font-size-small);
348
+ font-size: var(--font-size-s);
255
349
  padding: var(--space-s) var(--space-m);
256
350
  text-align: left;
257
351
  transition: background 0.2s ease;
@@ -259,12 +353,15 @@
259
353
  }
260
354
 
261
355
  .ap-card__cw-toggle:hover {
262
- background: var(--color-offset-active);
356
+ background: var(--color-offset-variant-darker);
263
357
  }
264
358
 
265
- /* Photo gallery */
359
+ /* ==========================================================================
360
+ Photo Gallery
361
+ ========================================================================== */
362
+
266
363
  .ap-card__gallery {
267
- border-radius: var(--border-radius);
364
+ border-radius: var(--border-radius-small);
268
365
  display: grid;
269
366
  gap: 2px;
270
367
  margin-bottom: var(--space-s);
@@ -277,7 +374,7 @@
277
374
  }
278
375
 
279
376
  .ap-card__gallery img {
280
- background: var(--color-offset);
377
+ background: var(--color-offset-variant);
281
378
  display: block;
282
379
  height: 200px;
283
380
  object-fit: cover;
@@ -285,7 +382,7 @@
285
382
  }
286
383
 
287
384
  .ap-card__gallery-link--more::after {
288
- background: rgba(0, 0, 0, 0.5);
385
+ background: hsl(var(--tint-neutral) 10% / 0.5);
289
386
  bottom: 0;
290
387
  content: "";
291
388
  left: 0;
@@ -295,7 +392,7 @@
295
392
  }
296
393
 
297
394
  .ap-card__gallery-more {
298
- color: #fff;
395
+ color: var(--color-neutral99);
299
396
  font-size: 1.5em;
300
397
  font-weight: 600;
301
398
  left: 50%;
@@ -315,12 +412,12 @@
315
412
  max-height: 400px;
316
413
  }
317
414
 
318
- /* 2 photos - side by side */
415
+ /* 2 photos side by side */
319
416
  .ap-card__gallery--2 {
320
417
  grid-template-columns: 1fr 1fr;
321
418
  }
322
419
 
323
- /* 3 photos - one large, two small */
420
+ /* 3 photos one large, two small */
324
421
  .ap-card__gallery--3 {
325
422
  grid-template-columns: 2fr 1fr;
326
423
  grid-template-rows: 1fr 1fr;
@@ -331,24 +428,30 @@
331
428
  height: 100%;
332
429
  }
333
430
 
334
- /* 4+ photos - 2x2 grid */
431
+ /* 4+ photos 2x2 grid */
335
432
  .ap-card__gallery--4 {
336
433
  grid-template-columns: 1fr 1fr;
337
434
  grid-template-rows: 1fr 1fr;
338
435
  }
339
436
 
340
- /* Video embed */
437
+ /* ==========================================================================
438
+ Video Embed
439
+ ========================================================================== */
440
+
341
441
  .ap-card__video {
342
442
  margin-bottom: var(--space-s);
343
443
  }
344
444
 
345
445
  .ap-card__video video {
346
- border-radius: var(--border-radius);
446
+ border-radius: var(--border-radius-small);
347
447
  max-height: 400px;
348
448
  width: 100%;
349
449
  }
350
450
 
351
- /* Audio player */
451
+ /* ==========================================================================
452
+ Audio Player
453
+ ========================================================================== */
454
+
352
455
  .ap-card__audio {
353
456
  margin-bottom: var(--space-s);
354
457
  }
@@ -357,7 +460,10 @@
357
460
  width: 100%;
358
461
  }
359
462
 
360
- /* Tags */
463
+ /* ==========================================================================
464
+ Tags
465
+ ========================================================================== */
466
+
361
467
  .ap-card__tags {
362
468
  display: flex;
363
469
  flex-wrap: wrap;
@@ -366,22 +472,25 @@
366
472
  }
367
473
 
368
474
  .ap-card__tag {
369
- background: var(--color-offset);
370
- border-radius: var(--border-radius);
371
- color: var(--color-text-muted);
372
- font-size: var(--font-size-small);
475
+ background: var(--color-offset-variant);
476
+ border-radius: var(--border-radius-large);
477
+ color: var(--color-on-offset);
478
+ font-size: var(--font-size-s);
373
479
  padding: 2px var(--space-xs);
374
480
  text-decoration: none;
375
481
  }
376
482
 
377
483
  .ap-card__tag:hover {
378
- background: var(--color-offset-active);
379
- color: var(--color-text);
484
+ background: var(--color-offset-variant-darker);
485
+ color: var(--color-on-background);
380
486
  }
381
487
 
382
- /* Interaction buttons */
488
+ /* ==========================================================================
489
+ Interaction Buttons
490
+ ========================================================================== */
491
+
383
492
  .ap-card__actions {
384
- border-top: 1px solid var(--color-offset);
493
+ border-top: var(--border-width-thin) solid var(--color-outline);
385
494
  display: flex;
386
495
  flex-wrap: wrap;
387
496
  gap: var(--space-s);
@@ -391,12 +500,12 @@
391
500
  .ap-card__action {
392
501
  align-items: center;
393
502
  background: transparent;
394
- border: 1px solid var(--color-offset);
395
- border-radius: var(--border-radius);
396
- color: var(--color-text-muted);
503
+ border: var(--border-width-thin) solid var(--color-outline);
504
+ border-radius: var(--border-radius-small);
505
+ color: var(--color-on-offset);
397
506
  cursor: pointer;
398
507
  display: inline-flex;
399
- font-size: var(--font-size-small);
508
+ font-size: var(--font-size-s);
400
509
  gap: var(--space-xs);
401
510
  padding: var(--space-xs) var(--space-s);
402
511
  text-decoration: none;
@@ -404,22 +513,22 @@
404
513
  }
405
514
 
406
515
  .ap-card__action:hover {
407
- background: var(--color-offset);
408
- border-color: var(--color-offset-active);
409
- color: var(--color-text);
516
+ background: var(--color-offset-variant);
517
+ border-color: var(--color-outline-variant);
518
+ color: var(--color-on-background);
410
519
  }
411
520
 
412
- /* Active interaction states */
521
+ /* Active interaction states — using Indiekit's color palette */
413
522
  .ap-card__action--like.ap-card__action--active {
414
- background: rgba(225, 29, 72, 0.1);
415
- border-color: #e11d48;
416
- color: #e11d48;
523
+ background: var(--color-red90);
524
+ border-color: var(--color-red45);
525
+ color: var(--color-red45);
417
526
  }
418
527
 
419
528
  .ap-card__action--boost.ap-card__action--active {
420
- background: rgba(22, 163, 74, 0.1);
421
- border-color: #16a34a;
422
- color: #16a34a;
529
+ background: var(--color-green90);
530
+ border-color: var(--color-green50);
531
+ color: var(--color-green50);
423
532
  }
424
533
 
425
534
  .ap-card__action:disabled {
@@ -429,8 +538,8 @@
429
538
 
430
539
  /* Error message */
431
540
  .ap-card__action-error {
432
- color: #e11d48;
433
- font-size: var(--font-size-small);
541
+ color: var(--color-error);
542
+ font-size: var(--font-size-s);
434
543
  width: 100%;
435
544
  }
436
545
 
@@ -439,7 +548,7 @@
439
548
  ========================================================================== */
440
549
 
441
550
  .ap-pagination {
442
- border-top: 1px solid var(--color-offset);
551
+ border-top: var(--border-width-thin) solid var(--color-outline);
443
552
  display: flex;
444
553
  gap: var(--space-m);
445
554
  justify-content: space-between;
@@ -462,15 +571,15 @@
462
571
 
463
572
  .ap-compose__context {
464
573
  background: var(--color-offset);
465
- border-left: 3px solid var(--color-primary);
466
- border-radius: var(--border-radius);
574
+ border-left: var(--border-width-thickest) solid var(--color-primary);
575
+ border-radius: var(--border-radius-small);
467
576
  margin-bottom: var(--space-m);
468
577
  padding: var(--space-m);
469
578
  }
470
579
 
471
580
  .ap-compose__context-label {
472
- color: var(--color-text-muted);
473
- font-size: var(--font-size-small);
581
+ color: var(--color-on-offset);
582
+ font-size: var(--font-size-s);
474
583
  margin-bottom: var(--space-xs);
475
584
  }
476
585
 
@@ -481,15 +590,15 @@
481
590
 
482
591
  .ap-compose__context-text {
483
592
  border: 0;
484
- font-size: var(--font-size-small);
485
- line-height: 1.5;
593
+ font-size: var(--font-size-s);
594
+ line-height: var(--line-height-loose);
486
595
  margin: var(--space-xs) 0;
487
596
  padding: 0;
488
597
  }
489
598
 
490
599
  .ap-compose__context-link {
491
- color: var(--color-text-muted);
492
- font-size: var(--font-size-small);
600
+ color: var(--color-on-offset);
601
+ font-size: var(--font-size-s);
493
602
  overflow: hidden;
494
603
  text-overflow: ellipsis;
495
604
  white-space: nowrap;
@@ -502,8 +611,8 @@
502
611
  }
503
612
 
504
613
  .ap-compose__mode {
505
- border: 1px solid var(--color-offset);
506
- border-radius: var(--border-radius);
614
+ border: var(--border-width-thin) solid var(--color-outline);
615
+ border-radius: var(--border-radius-small);
507
616
  display: flex;
508
617
  flex-direction: column;
509
618
  gap: var(--space-s);
@@ -522,9 +631,9 @@
522
631
  }
523
632
 
524
633
  .ap-compose__mode-hint {
525
- color: var(--color-text-muted);
634
+ color: var(--color-on-offset);
526
635
  display: block;
527
- font-size: var(--font-size-small);
636
+ font-size: var(--font-size-s);
528
637
  margin-left: 1.5em;
529
638
  width: 100%;
530
639
  }
@@ -534,11 +643,13 @@
534
643
  }
535
644
 
536
645
  .ap-compose__textarea {
537
- border: 1px solid var(--color-offset-active);
538
- border-radius: var(--border-radius);
646
+ background: var(--color-background);
647
+ border: var(--border-width-thick) solid var(--color-outline);
648
+ border-radius: var(--border-radius-small);
649
+ color: var(--color-on-background);
539
650
  font-family: inherit;
540
- font-size: var(--font-size-body);
541
- line-height: 1.6;
651
+ font-size: var(--font-size-m);
652
+ line-height: var(--line-height-prose);
542
653
  padding: var(--space-s);
543
654
  resize: vertical;
544
655
  width: 100%;
@@ -546,28 +657,28 @@
546
657
 
547
658
  .ap-compose__textarea:focus {
548
659
  border-color: var(--color-primary);
549
- outline: 2px solid var(--color-primary);
660
+ outline: var(--border-width-thick) solid var(--color-primary);
550
661
  outline-offset: -2px;
551
662
  }
552
663
 
553
664
  .ap-compose__counter {
554
- font-size: var(--font-size-small);
665
+ font-size: var(--font-size-s);
555
666
  padding-top: var(--space-xs);
556
667
  text-align: right;
557
668
  }
558
669
 
559
670
  .ap-compose__counter--warn {
560
- color: #d97706;
671
+ color: var(--color-yellow50);
561
672
  }
562
673
 
563
674
  .ap-compose__counter--over {
564
- color: #e11d48;
675
+ color: var(--color-error);
565
676
  font-weight: 600;
566
677
  }
567
678
 
568
679
  .ap-compose__syndication {
569
- border: 1px solid var(--color-offset);
570
- border-radius: var(--border-radius);
680
+ border: var(--border-width-thin) solid var(--color-outline);
681
+ border-radius: var(--border-radius-small);
571
682
  display: flex;
572
683
  flex-direction: column;
573
684
  gap: var(--space-xs);
@@ -593,10 +704,10 @@
593
704
  .ap-compose__submit {
594
705
  background: var(--color-primary);
595
706
  border: 0;
596
- border-radius: var(--border-radius);
597
- color: #fff;
707
+ border-radius: var(--border-radius-small);
708
+ color: var(--color-on-primary, var(--color-neutral99));
598
709
  cursor: pointer;
599
- font-size: var(--font-size-body);
710
+ font-size: var(--font-size-m);
600
711
  font-weight: 600;
601
712
  padding: var(--space-s) var(--space-l);
602
713
  }
@@ -606,12 +717,12 @@
606
717
  }
607
718
 
608
719
  .ap-compose__cancel {
609
- color: var(--color-text-muted);
720
+ color: var(--color-on-offset);
610
721
  text-decoration: none;
611
722
  }
612
723
 
613
724
  .ap-compose__cancel:hover {
614
- color: var(--color-text);
725
+ color: var(--color-on-background);
615
726
  text-decoration: underline;
616
727
  }
617
728
 
@@ -621,17 +732,17 @@
621
732
 
622
733
  .ap-notification {
623
734
  align-items: flex-start;
624
- background: var(--color-background);
625
- border: 1px solid var(--color-offset);
626
- border-radius: var(--border-radius);
735
+ background: var(--color-offset);
736
+ border: var(--border-width-thin) solid var(--color-outline);
737
+ border-radius: var(--border-radius-small);
627
738
  display: flex;
628
739
  gap: var(--space-s);
629
740
  padding: var(--space-m);
630
741
  }
631
742
 
632
743
  .ap-notification--unread {
633
- border-color: rgba(255, 204, 0, 0.5);
634
- box-shadow: 0 0 8px 0 rgba(255, 204, 0, 0.3);
744
+ border-color: var(--color-yellow50);
745
+ box-shadow: 0 0 8px 0 hsl(var(--tint-yellow) 50% / 0.3);
635
746
  }
636
747
 
637
748
  .ap-notification__icon {
@@ -649,13 +760,13 @@
649
760
  }
650
761
 
651
762
  .ap-notification__action {
652
- color: var(--color-text-muted);
763
+ color: var(--color-on-offset);
653
764
  }
654
765
 
655
766
  .ap-notification__target {
656
- color: var(--color-text-muted);
767
+ color: var(--color-on-offset);
657
768
  display: block;
658
- font-size: var(--font-size-small);
769
+ font-size: var(--font-size-s);
659
770
  margin-top: var(--space-xs);
660
771
  overflow: hidden;
661
772
  text-overflow: ellipsis;
@@ -663,17 +774,17 @@
663
774
  }
664
775
 
665
776
  .ap-notification__excerpt {
666
- background: var(--color-offset);
667
- border-radius: var(--border-radius);
668
- font-size: var(--font-size-small);
777
+ background: var(--color-offset-variant);
778
+ border-radius: var(--border-radius-small);
779
+ font-size: var(--font-size-s);
669
780
  margin-top: var(--space-xs);
670
781
  padding: var(--space-xs) var(--space-s);
671
782
  }
672
783
 
673
784
  .ap-notification__time {
674
- color: var(--color-text-muted);
785
+ color: var(--color-on-offset);
675
786
  flex-shrink: 0;
676
- font-size: var(--font-size-small);
787
+ font-size: var(--font-size-xs);
677
788
  }
678
789
 
679
790
  /* ==========================================================================
@@ -681,7 +792,7 @@
681
792
  ========================================================================== */
682
793
 
683
794
  .ap-profile__header {
684
- border-radius: var(--border-radius);
795
+ border-radius: var(--border-radius-small);
685
796
  height: 200px;
686
797
  margin-bottom: var(--space-m);
687
798
  overflow: hidden;
@@ -702,7 +813,7 @@
702
813
  }
703
814
 
704
815
  .ap-profile__avatar {
705
- border: 3px solid var(--color-background);
816
+ border: var(--border-width-thickest) solid var(--color-background);
706
817
  border-radius: 50%;
707
818
  height: 80px;
708
819
  object-fit: cover;
@@ -711,8 +822,8 @@
711
822
 
712
823
  .ap-profile__avatar--placeholder {
713
824
  align-items: center;
714
- background: var(--color-offset);
715
- color: var(--color-text-muted);
825
+ background: var(--color-offset-variant);
826
+ color: var(--color-on-offset);
716
827
  display: flex;
717
828
  font-size: 2em;
718
829
  font-weight: 600;
@@ -720,17 +831,17 @@
720
831
  }
721
832
 
722
833
  .ap-profile__name {
723
- font-size: var(--font-size-heading-3);
834
+ font-size: var(--font-size-xl);
724
835
  margin-bottom: var(--space-xs);
725
836
  }
726
837
 
727
838
  .ap-profile__handle {
728
- color: var(--color-text-muted);
839
+ color: var(--color-on-offset);
729
840
  margin-bottom: var(--space-s);
730
841
  }
731
842
 
732
843
  .ap-profile__bio {
733
- line-height: 1.6;
844
+ line-height: var(--line-height-prose);
734
845
  margin-bottom: var(--space-s);
735
846
  }
736
847
 
@@ -747,11 +858,11 @@
747
858
 
748
859
  .ap-profile__action {
749
860
  background: transparent;
750
- border: 1px solid var(--color-offset-active);
751
- border-radius: var(--border-radius);
752
- color: var(--color-text);
861
+ border: var(--border-width-thin) solid var(--color-outline);
862
+ border-radius: var(--border-radius-small);
863
+ color: var(--color-on-background);
753
864
  cursor: pointer;
754
- font-size: var(--font-size-small);
865
+ font-size: var(--font-size-s);
755
866
  padding: var(--space-xs) var(--space-m);
756
867
  text-decoration: none;
757
868
  }
@@ -763,12 +874,12 @@
763
874
  .ap-profile__action--follow.ap-profile__action--active {
764
875
  background: var(--color-primary);
765
876
  border-color: var(--color-primary);
766
- color: #fff;
877
+ color: var(--color-on-primary, var(--color-neutral99));
767
878
  }
768
879
 
769
880
  .ap-profile__action--danger:hover {
770
- border-color: #e11d48;
771
- color: #e11d48;
881
+ border-color: var(--color-error);
882
+ color: var(--color-error);
772
883
  }
773
884
 
774
885
  .ap-profile__posts {
@@ -776,8 +887,8 @@
776
887
  }
777
888
 
778
889
  .ap-profile__posts h3 {
779
- border-bottom: 1px solid var(--color-offset);
780
- font-size: var(--font-size-heading-4);
890
+ border-bottom: var(--border-width-thin) solid var(--color-outline);
891
+ font-size: var(--font-size-l);
781
892
  margin-bottom: var(--space-m);
782
893
  padding-bottom: var(--space-s);
783
894
  }
@@ -791,7 +902,7 @@
791
902
  }
792
903
 
793
904
  .ap-moderation__section h2 {
794
- font-size: var(--font-size-heading-4);
905
+ font-size: var(--font-size-l);
795
906
  margin-bottom: var(--space-s);
796
907
  }
797
908
 
@@ -803,7 +914,7 @@
803
914
 
804
915
  .ap-moderation__entry {
805
916
  align-items: center;
806
- border-bottom: 1px solid var(--color-offset);
917
+ border-bottom: var(--border-width-thin) solid var(--color-outline);
807
918
  display: flex;
808
919
  gap: var(--space-s);
809
920
  justify-content: space-between;
@@ -819,18 +930,18 @@
819
930
 
820
931
  .ap-moderation__remove {
821
932
  background: transparent;
822
- border: 1px solid var(--color-offset-active);
823
- border-radius: var(--border-radius);
824
- color: var(--color-text-muted);
933
+ border: var(--border-width-thin) solid var(--color-outline);
934
+ border-radius: var(--border-radius-small);
935
+ color: var(--color-on-offset);
825
936
  cursor: pointer;
826
937
  flex-shrink: 0;
827
- font-size: var(--font-size-small);
938
+ font-size: var(--font-size-s);
828
939
  padding: var(--space-xs) var(--space-s);
829
940
  }
830
941
 
831
942
  .ap-moderation__remove:hover {
832
- border-color: #e11d48;
833
- color: #e11d48;
943
+ border-color: var(--color-error);
944
+ color: var(--color-error);
834
945
  }
835
946
 
836
947
  .ap-moderation__add-form {
@@ -839,24 +950,27 @@
839
950
  }
840
951
 
841
952
  .ap-moderation__input {
842
- border: 1px solid var(--color-offset-active);
843
- border-radius: var(--border-radius);
953
+ background: var(--color-background);
954
+ border: var(--border-width-thick) solid var(--color-outline);
955
+ border-radius: var(--border-radius-small);
956
+ color: var(--color-on-background);
844
957
  flex: 1;
845
- font-size: var(--font-size-body);
958
+ font-size: var(--font-size-m);
846
959
  padding: var(--space-xs) var(--space-s);
847
960
  }
848
961
 
849
962
  .ap-moderation__add-btn {
850
963
  background: var(--color-offset);
851
- border: 1px solid var(--color-offset-active);
852
- border-radius: var(--border-radius);
964
+ border: var(--border-width-thin) solid var(--color-outline);
965
+ border-radius: var(--border-radius-small);
966
+ color: var(--color-on-background);
853
967
  cursor: pointer;
854
- font-size: var(--font-size-body);
968
+ font-size: var(--font-size-m);
855
969
  padding: var(--space-xs) var(--space-m);
856
970
  }
857
971
 
858
972
  .ap-moderation__add-btn:hover {
859
- background: var(--color-offset-active);
973
+ background: var(--color-offset-variant);
860
974
  }
861
975
 
862
976
  /* ==========================================================================
@@ -321,6 +321,11 @@ export function registerInboxListeners(inboxChain, options) {
321
321
  const object = await announce.getObject();
322
322
  if (!object) return;
323
323
 
324
+ // Skip non-content objects (Lemmy/PieFed like/create activities
325
+ // that resolve to activity IDs instead of actual Note/Article posts)
326
+ const hasContent = object.content?.toString() || object.name?.toString();
327
+ if (!hasContent) return;
328
+
324
329
  // Get booster actor info
325
330
  const boosterActor = await announce.getActor();
326
331
  const boosterInfo = await extractActorInfo(boosterActor);
@@ -446,7 +451,9 @@ export function registerInboxListeners(inboxChain, options) {
446
451
  const following = await collections.ap_following.findOne({ actorUrl });
447
452
  if (following) {
448
453
  try {
449
- const timelineItem = await extractObjectData(object);
454
+ const timelineItem = await extractObjectData(object, {
455
+ actorFallback: actorObj,
456
+ });
450
457
  await addTimelineItem(collections, timelineItem);
451
458
  } catch (error) {
452
459
  // Log extraction errors but don't fail the entire handler
@@ -87,6 +87,7 @@ export async function extractActorInfo(actor) {
87
87
  * @param {object} options - Extraction options
88
88
  * @param {object} [options.boostedBy] - Actor info for boosts
89
89
  * @param {Date} [options.boostedAt] - Boost timestamp
90
+ * @param {object} [options.actorFallback] - Fedify actor to use when object.getAttributedTo() fails
90
91
  * @returns {Promise<object>} Timeline item data
91
92
  */
92
93
  export async function extractObjectData(object, options = {}) {
@@ -127,7 +128,7 @@ export async function extractObjectData(object, options = {}) {
127
128
  ? String(object.published)
128
129
  : new Date().toISOString();
129
130
 
130
- // Extract author — use async getAttributedTo() for Fedify objects
131
+ // Extract author — try multiple strategies in order of reliability
131
132
  let authorObj = null;
132
133
  try {
133
134
  if (typeof object.getAttributedTo === "function") {
@@ -135,10 +136,39 @@ export async function extractObjectData(object, options = {}) {
135
136
  authorObj = Array.isArray(attr) ? attr[0] : attr;
136
137
  }
137
138
  } catch {
138
- // Fallback: try direct property access for plain objects
139
+ // getAttributedTo() failed (Authorized Fetch, unreachable, etc.)
140
+ }
141
+ // If getAttributedTo() returned nothing, use the actor from the wrapping activity
142
+ if (!authorObj && options.actorFallback) {
143
+ authorObj = options.actorFallback;
144
+ }
145
+ // Try direct property access for plain objects
146
+ if (!authorObj) {
139
147
  authorObj = object.attribution || object.attributedTo || null;
140
148
  }
141
- const author = await extractActorInfo(authorObj);
149
+
150
+ let author;
151
+ if (authorObj) {
152
+ author = await extractActorInfo(authorObj);
153
+ } else {
154
+ // Last resort: use attributionIds (non-fetching) to get at least a URL
155
+ const attrIds = object.attributionIds;
156
+ if (attrIds && attrIds.length > 0) {
157
+ const authorUrl = attrIds[0].href;
158
+ const authorHostname = new URL(authorUrl).hostname;
159
+ // Extract username from URL pattern like /users/name or /@name
160
+ const pathMatch = new URL(authorUrl).pathname.match(/\/@?([^/]+)/);
161
+ const username = pathMatch ? pathMatch[1] : "";
162
+ author = {
163
+ name: username || authorHostname,
164
+ url: authorUrl,
165
+ photo: "",
166
+ handle: username ? `@${username}@${authorHostname}` : "",
167
+ };
168
+ } else {
169
+ author = { name: "Unknown", url: "", photo: "", handle: "" };
170
+ }
171
+ }
142
172
 
143
173
  // Extract tags/categories
144
174
  const category = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
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",
@@ -1,6 +1,12 @@
1
1
  {# Timeline item card partial - reusable across timeline and profile views #}
2
2
 
3
- <article class="ap-card">
3
+ {# Skip empty cards (e.g. Lemmy/PieFed activity IDs with no actual content) #}
4
+ {% set hasCardContent = item.content and (item.content.html or item.content.text) %}
5
+ {% set hasCardTitle = item.name %}
6
+ {% set hasCardMedia = (item.photo and item.photo.length > 0) or (item.video and item.video.length > 0) or (item.audio and item.audio.length > 0) %}
7
+ {% if hasCardContent or hasCardTitle or hasCardMedia %}
8
+
9
+ <article class="ap-card{% if item.type %} ap-card--{{ item.type }}{% endif %}{% if item.inReplyTo %} ap-card--reply{% endif %}">
4
10
  {# Boost header if this is a boosted post #}
5
11
  {% if item.type == "boost" and item.boostedBy %}
6
12
  <div class="ap-card__boost">
@@ -167,3 +173,5 @@
167
173
  <div x-show="error" x-text="error" class="ap-card__action-error" x-transition></div>
168
174
  </footer>
169
175
  </article>
176
+
177
+ {% endif %}{# end hasCardContent/hasCardTitle/hasCardMedia guard #}