me.linkna.linkinbio

linkna.me

Schema Diff

+13 -4

From

CID
bafyreiaslbzydhq...
Indexed At
2026-03-19 20:26 UTC
View this version

To

CID
bafyreidwzodhqyu...
Indexed At
2026-04-21 15:34 UTC
View this version

Compatibility Analysis

Breaking Changes Detected

2 breaking changes, 2 non-breaking changes.

Breaking Changes (2)
  • KindChanged KindChanged { vertex_id: "me.linkna.linkinbio#widgetMap.latitude", old_kind: "object", new_kind: "string" }
  • KindChanged KindChanged { vertex_id: "me.linkna.linkinbio#widgetMap.longitude", old_kind: "object", new_kind: "string" }
Non-Breaking Changes (2)
  • AddedVertex AddedVertex { vertex_id: "me.linkna.linkinbio#widgetMap.mapImageRef" }
  • AddedEdge AddedEdge { src: "me.linkna.linkinbio#widgetMap", tgt: "me.linkna.linkinbio#widgetMap.mapImageRef", kind: "prop", name: Some("mapImageRef") }

Migration Guidance

Added Elements

  • AddedVertex { vertex_id: "me.linkna.linkinbio#widgetMap.mapImageRef" }

Additional Notes

  • Breaking: KindChanged { vertex_id: "me.linkna.linkinbio#widgetMap.latitude", old_kind: "object", new_kind: "string" }
  • Breaking: KindChanged { vertex_id: "me.linkna.linkinbio#widgetMap.longitude", old_kind: "object", new_kind: "string" }
  • Non-breaking: AddedEdge { src: "me.linkna.linkinbio#widgetMap", tgt: "me.linkna.linkinbio#widgetMap.mapImageRef", kind: "prop", name: Some("mapImageRef") }
1 1
{
2 2
  "id": "me.linkna.linkinbio",
3 3
  "defs": {
4 4
    "main": {
5 5
      "key": "literal:self",
6 6
      "type": "record",
7 7
      "record": {
8 8
        "type": "object",
9 9
        "required": [
10 10
          "cards"
11 11
        ],
12 12
        "properties": {
13 13
          "cards": {
14 14
            "type": "array",
15 15
            "items": {
16 16
              "refs": [
17 17
                "#linkCard",
18 18
                "#headerCard",
19 19
                "#widgetGoodreads",
20 20
                "#widgetGithub",
21 21
                "#widgetTealfm",
22 22
                "#widgetLastfm",
23 23
                "#widgetClock",
24 24
                "#widgetYoutube",
25 25
                "#widgetPatreon",
26 26
                "#widgetSubstack",
27 27
                "#widgetBeehiiv",
28 28
                "#widgetVideo",
29 29
                "#widgetTwitch",
30 30
                "#widgetDiscord",
31 31
                "#widgetReddit",
32 32
                "#widgetMedium",
33 33
                "#widgetMap"
34 34
              ],
35 35
              "type": "union"
36 36
            },
37 37
            "description": "Ordered list of cards on the page."
38 38
          },
39 39
          "theme": {
40 40
            "type": "string",
41 41
            "description": "Deprecated: use themeConfig instead."
42 42
          },
43 43
          "socialIcons": {
44 44
            "type": "array",
45 45
            "items": {
46 46
              "ref": "#socialIcon",
47 47
              "type": "ref"
48 48
            },
49 49
            "description": "Social media icons displayed on the profile."
50 50
          },
51 51
          "themeConfig": {
52 52
            "ref": "#themeConfig",
53 53
            "type": "ref"
54 54
          },
55 55
          "connectedServices": {
56 56
            "ref": "#connectedServices",
57 57
            "type": "ref"
58 58
          }
59 59
        }
60 60
      },
61 61
      "description": "A user's link-in-bio page containing cards, theme configuration, and social icons."
62 62
    },
63 63
    "linkCard": {
64 64
      "type": "object",
65 65
      "properties": {
66 66
        "id": {
67 67
          "type": "string",
68 68
          "description": "Unique identifier for the card."
69 69
        },
70 70
        "url": {
71 71
          "type": "string",
72 72
          "format": "uri",
73 73
          "description": "URL the card links to."
74 74
        },
75 75
        "text": {
76 76
          "type": "string",
77 77
          "maxLength": 100,
78 78
          "description": "Display text for the card (falls back to URL domain if empty)."
79 79
        },
80 80
        "type": {
81 81
          "type": "string",
82 82
          "description": "Card type discriminator. Value: 'link' or absent for link cards."
83 83
        },
84 84
        "enabled": {
85 85
          "type": "boolean",
86 86
          "description": "Whether the card is visible on the public page. Defaults to true."
87 87
        },
88 88
        "highlighted": {
89 89
          "type": "boolean",
90 90
          "description": "Whether the card is visually highlighted."
91 91
        }
92 92
      },
93 93
      "description": "A standard link card."
94 94
    },
95 95
    "widgetMap": {
96 96
      "type": "object",
97 97
      "required": [
98 98
        "id",
99 99
        "type",
100 100
        "locationName",
101 101
        "latitude",
102 102
        "longitude"
103 103
      ],
104 104
      "properties": {
105 105
        "id": {
106 106
          "type": "string",
107 107
          "description": "Unique identifier for the card."
108 108
        },
109 109
        "size": {
110 110
          "type": "string",
111 111
          "description": "Widget display size. Default: 1x1.",
112 112
          "knownValues": [
113 113
            "1x1",
114 114
            "1x2",
115 115
            "2x2",
116 116
            "2x1"
117 117
          ]
118 118
        },
119 119
        "type": {
120 120
          "type": "string",
121 121
          "const": "map",
122 122
          "description": "Card type discriminator."
123 123
        },
124 124
        "zoom": {
125 125
          "type": "integer",
126 126
          "description": "Map zoom level (1-18). Default: 12."
127 127
        },
128 128
        "style": {
129 129
          "type": "string",
130 130
          "description": "Map style. Default: light.",
131 131
          "knownValues": [
132 132
            "light",
133 133
            "dark"
134 134
          ]
135 135
        },
136 136
        "enabled": {
137 137
          "type": "boolean",
138 138
          "description": "Whether the widget is visible on the public page. Defaults to true."
139 139
        },
140 140
        "latitude": {
141 -
          "type": "number",
142 -
          "description": "Latitude coordinate."
141 +
          "type": "string",
142 +
          "description": "Latitude coordinate as string (AT Protocol does not support floats)."
143 143
        },
144 144
        "longitude": {
145 -
          "type": "number",
146 -
          "description": "Longitude coordinate."
145 +
          "type": "string",
146 +
          "description": "Longitude coordinate as string (AT Protocol does not support floats)."
147 +
        },
148 +
        "mapImageRef": {
149 +
          "type": "blob",
150 +
          "accept": [
151 +
            "image/png",
152 +
            "image/jpeg"
153 +
          ],
154 +
          "maxSize": 5000000,
155 +
          "description": "Cached static map image blob to avoid repeated Stadia Maps API calls."
147 156
        },
148 157
        "locationName": {
149 158
          "type": "string",
150 159
          "description": "Display name for the location."
151 160
        }
152 161
      },
153 162
      "description": "A map location widget card."
154 163
    },
155 164
    "headerCard": {
156 165
      "type": "object",
157 166
      "required": [
158 167
        "id",
159 168
        "type",
160 169
        "text"
161 170
      ],
162 171
      "properties": {
163 172
        "id": {
164 173
          "type": "string",
165 174
          "description": "Unique identifier for the card."
166 175
        },
167 176
        "text": {
168 177
          "type": "string",
169 178
          "maxLength": 100,
170 179
          "description": "Header text displayed as a section divider."
171 180
        },
172 181
        "type": {
173 182
          "type": "string",
174 183
          "const": "header",
175 184
          "description": "Card type discriminator."
176 185
        },
177 186
        "enabled": {
178 187
          "type": "boolean",
179 188
          "description": "Whether the header is visible on the public page. Defaults to true."
180 189
        },
181 190
        "alignment": {
182 191
          "enum": [
183 192
            "left",
184 193
            "center",
185 194
            "right"
186 195
          ],
187 196
          "type": "string",
188 197
          "description": "Text alignment for the header. Defaults to the user's button alignment."
189 198
        }
190 199
      },
191 200
      "description": "A section header/divider card."
192 201
    },
193 202
    "redditPost": {
194 203
      "type": "object",
195 204
      "required": [
196 205
        "title",
197 206
        "subreddit",
198 207
        "url",
199 208
        "score",
200 209
        "numComments"
201 210
      ],
202 211
      "properties": {
203 212
        "url": {
204 213
          "type": "string",
205 214
          "description": "Post URL."
206 215
        },
207 216
        "score": {
208 217
          "type": "integer",
209 218
          "description": "Post score."
210 219
        },
211 220
        "title": {
212 221
          "type": "string",
213 222
          "description": "Post title."
214 223
        },
215 224
        "subreddit": {
216 225
          "type": "string",
217 226
          "description": "Subreddit name."
218 227
        },
219 228
        "numComments": {
220 229
          "type": "integer",
221 230
          "description": "Number of comments."
222 231
        }
223 232
      },
224 233
      "description": "A Reddit post."
225 234
    },
226 235
    "socialIcon": {
227 236
      "type": "object",
228 237
      "required": [
229 238
        "id",
230 239
        "platform",
231 240
        "url"
232 241
      ],
233 242
      "properties": {
234 243
        "id": {
235 244
          "type": "string",
236 245
          "description": "Unique identifier for the icon."
237 246
        },
238 247
        "url": {
239 248
          "type": "string",
240 249
          "format": "uri",
241 250
          "description": "URL to the user's profile on this platform."
242 251
        },
243 252
        "platform": {
244 253
          "type": "string",
245 254
          "description": "Platform identifier (e.g., 'bluesky', 'twitter', 'instagram')."
246 255
        }
247 256
      },
248 257
      "description": "A social media icon link."
249 258
    },
250 259
    "tealfmPlay": {
251 260
      "type": "object",
252 261
      "required": [
253 262
        "trackName",
254 263
        "artistName"
255 264
      ],
256 265
      "properties": {
257 266
        "coverUrl": {
258 267
          "type": "string",
259 268
          "description": "URL to the album cover image (from Cover Art Archive)."
260 269
        },
261 270
        "originUrl": {
262 271
          "type": "string",
263 272
          "description": "URL to the track on the original music service."
264 273
        },
265 274
        "trackName": {
266 275
          "type": "string",
267 276
          "description": "Track/song name."
268 277
        },
269 278
        "artistName": {
270 279
          "type": "string",
271 280
          "description": "Artist name(s)."
272 281
        },
273 282
        "playedTime": {
274 283
          "type": "string",
275 284
          "format": "datetime",
276 285
          "description": "When the track was played."
277 286
        },
278 287
        "releaseName": {
279 288
          "type": "string",
280 289
          "description": "Album/release name."
281 290
        }
282 291
      },
283 292
      "description": "A recently played track from teal.fm."
284 293
    },
285 294
    "themeConfig": {
286 295
      "type": "object",
287 296
      "properties": {
288 297
        "preset": {
289 298
          "type": "string",
290 299
          "description": "Theme preset ID (e.g., 'custom', 'agate', 'air', 'astrid', 'aura')."
291 300
        },
292 301
        "fontSize": {
293 302
          "type": "string",
294 303
          "description": "Font size. Default: md.",
295 304
          "knownValues": [
296 305
            "sm",
297 306
            "md",
298 307
            "lg"
299 308
          ]
300 309
        },
301 310
        "particles": {
302 311
          "type": "string",
303 312
          "description": "Background particle effect. Default: none.",
304 313
          "knownValues": [
305 314
            "none",
306 315
            "white",
307 316
            "blue",
308 317
            "leaves",
309 318
            "grid-sm",
310 319
            "grid-lg"
311 320
          ]
312 321
        },
313 322
        "cardShadow": {
314 323
          "type": "string",
315 324
          "description": "Card/button shadow intensity.",
316 325
          "knownValues": [
317 326
            "none",
318 327
            "sm",
319 328
            "md",
320 329
            "lg",
321 330
            "xl"
322 331
          ]
323 332
        },
324 333
        "fontFamily": {
325 334
          "type": "string",
326 335
          "description": "Font family for the page."
327 336
        },
328 337
        "cursorStyle": {
329 338
          "type": "string",
330 339
          "description": "Custom cursor style. Default: default.",
331 340
          "knownValues": [
332 341
            "default",
333 342
            "hello-kitty"
334 343
          ]
335 344
        },
336 345
        "showBranding": {
337 346
          "type": "boolean",
338 347
          "description": "Whether Linkname branding is visible. Default: true."
339 348
        },
340 349
        "cardTextColor": {
341 350
          "type": "string",
342 351
          "description": "Card/button text color."
343 352
        },
344 353
        "cardHoverColor": {
345 354
          "type": "string",
346 355
          "description": "Card/button border color on hover."
347 356
        },
348 357
        "cursorSparkles": {
349 358
          "type": "boolean",
350 359
          "description": "Whether cursor has sparkle trail. Default: false."
351 360
        },
352 361
        "backgroundColor": {
353 362
          "type": "string",
354 363
          "description": "Page background color."
355 364
        },
356 365
        "backgroundImage": {
357 366
          "type": "blob",
358 367
          "accept": [
359 368
            "image/png",
360 369
            "image/jpeg",
361 370
            "image/webp"
362 371
          ],
363 372
          "maxSize": 10000000,
364 373
          "description": "Background wallpaper image blob."
365 374
        },
366 375
        "buttonAlignment": {
367 376
          "type": "string",
368 377
          "description": "Button content alignment. Default: left.",
369 378
          "knownValues": [
370 379
            "left",
371 380
            "right"
372 381
          ]
373 382
        },
374 383
        "buttonHoverGlow": {
375 384
          "type": "boolean",
376 385
          "description": "Whether buttons glow on hover. Default: false."
377 386
        },
378 387
        "cardBorderColor": {
379 388
          "type": "string",
380 389
          "description": "Card/button border color."
381 390
        },
382 391
        "cardBorderRadius": {
383 392
          "type": "string",
384 393
          "description": "Card/button border radius.",
385 394
          "knownValues": [
386 395
            "none",
387 396
            "sm",
388 397
            "md",
389 398
            "lg",
390 399
            "full"
391 400
          ]
392 401
        },
393 402
        "contentCardColor": {
394 403
          "type": "string",
395 404
          "description": "Card container background color."
396 405
        },
397 406
        "profileAlignment": {
398 407
          "type": "string",
399 408
          "description": "Profile section alignment. Default: center.",
400 409
          "knownValues": [
401 410
            "left",
402 411
            "center",
403 412
            "right"
404 413
          ]
405 414
        },
406 415
        "profileTextColor": {
407 416
          "type": "string",
408 417
          "description": "Profile section text color."
409 418
        },
410 419
        "socialIconsColor": {
411 420
          "type": "string",
412 421
          "description": "Social icons color."
413 422
        },
414 423
        "socialIconsShape": {
415 424
          "type": "string",
416 425
          "description": "Social icons container shape. Default: dock.",
417 426
          "knownValues": [
418 427
            "dock",
419 428
            "sphere"
420 429
          ]
421 430
        },
422 431
        "backgroundGradient": {
423 432
          "type": "string",
424 433
          "description": "CSS gradient for the page background."
425 434
        },
426 435
        "profilePictureSize": {
427 436
          "type": "string",
428 437
          "description": "Profile picture size. Default: sm.",
429 438
          "knownValues": [
430 439
            "sm",
431 440
            "md",
432 441
            "lg"
433 442
          ]
434 443
        },
435 444
        "cardBackgroundColor": {
436 445
          "type": "string",
437 446
          "description": "Individual card/button background color."
438 447
        },
439 448
        "profilePictureShape": {
440 449
          "type": "string",
441 450
          "description": "Profile picture shape. Default: circle.",
442 451
          "knownValues": [
443 452
            "circle",
444 453
            "rounded",
445 454
            "square"
446 455
          ]
447 456
        },
448 457
        "buttonHoverGlowColor": {
449 458
          "type": "string",
450 459
          "description": "Color for the button hover glow effect."
451 460
        },
452 461
        "backgroundImageCredit": {
453 462
          "type": "string",
454 463
          "format": "uri",
455 464
          "description": "Optional credit URL for the wallpaper image artist. Stored for attribution purposes only, not displayed on the public page."
456 465
        },
457 466
        "socialIconsShapeColor": {
458 467
          "type": "string",
459 468
          "description": "Background color for social icons dock/sphere."
460 469
        },
461 470
        "socialIconsBorderColor": {
462 471
          "type": "string",
463 472
          "description": "Border color for social icons dock/sphere."
464 473
        }
465 474
      },
466 475
      "description": "Theme configuration for the page appearance."
467 476
    },
468 477
    "widgetClock": {
469 478
      "type": "object",
470 479
      "required": [
471 480
        "id",
472 481
        "type",
473 482
        "mode"
474 483
      ],
475 484
      "properties": {
476 485
        "id": {
477 486
          "type": "string",
478 487
          "description": "Unique identifier for the card."
479 488
        },
480 489
        "mode": {
481 490
          "type": "string",
482 491
          "description": "Widget mode: clock (timezone display), countdown (to a target date), or timer (interactive stopwatch).",
483 492
          "knownValues": [
484 493
            "clock",
485 494
            "countdown",
486 495
            "timer"
487 496
          ]
488 497
        },
489 498
        "size": {
490 499
          "type": "string",
491 500
          "description": "Widget display size. Default: 1x1.",
492 501
          "knownValues": [
493 502
            "1x1",
494 503
            "1x2",
495 504
            "2x2",
496 505
            "2x1"
497 506
          ]
498 507
        },
499 508
        "type": {
500 509
          "type": "string",
501 510
          "const": "clock",
502 511
          "description": "Card type discriminator."
503 512
        },
504 513
        "label": {
505 514
          "type": "string",
506 515
          "maxLength": 100,
507 516
          "description": "Optional display label for the widget."
508 517
        },
509 518
        "enabled": {
510 519
          "type": "boolean",
511 520
          "description": "Whether the widget is visible on the public page. Defaults to true."
512 521
        },
513 522
        "timezone": {
514 523
          "type": "string",
515 524
          "description": "IANA timezone identifier for clock mode (e.g. 'America/New_York')."
516 525
        },
517 526
        "targetDate": {
518 527
          "type": "string",
519 528
          "format": "datetime",
520 529
          "description": "ISO 8601 target date for countdown mode."
521 530
        }
522 531
      },
523 532
      "description": "A clock, countdown, or stopwatch timer widget card."
524 533
    },
525 534
    "widgetVideo": {
526 535
      "type": "object",
527 536
      "required": [
528 537
        "id",
529 538
        "type",
530 539
        "videoBlobRef",
531 540
        "did"
532 541
      ],
533 542
      "properties": {
534 543
        "id": {
535 544
          "type": "string",
536 545
          "description": "Unique identifier for the card."
537 546
        },
538 547
        "alt": {
539 548
          "type": "string",
540 549
          "maxLength": 1000,
541 550
          "description": "Alt text for accessibility."
542 551
        },
543 552
        "did": {
544 553
          "type": "string",
545 554
          "description": "Author DID for constructing PDS blob URLs."
546 555
        },
547 556
        "loop": {
548 557
          "type": "boolean",
549 558
          "description": "Whether the video loops. Defaults to true."
550 559
        },
551 560
        "size": {
552 561
          "type": "string",
553 562
          "description": "Widget display size. Default: 1x1.",
554 563
          "knownValues": [
555 564
            "1x1",
556 565
            "1x2",
557 566
            "2x2",
558 567
            "2x1"
559 568
          ]
560 569
        },
561 570
        "type": {
562 571
          "type": "string",
563 572
          "const": "video",
564 573
          "description": "Card type discriminator."
565 574
        },
566 575
        "muted": {
567 576
          "type": "boolean",
568 577
          "description": "Whether the video is muted. Defaults to true."
569 578
        },
570 579
        "title": {
571 580
          "type": "string",
572 581
          "maxLength": 100,
573 582
          "description": "Video title overlay."
574 583
        },
575 584
        "enabled": {
576 585
          "type": "boolean",
577 586
          "description": "Whether the widget is visible on the public page. Defaults to true."
578 587
        },
579 588
        "createdAt": {
580 589
          "type": "string",
581 590
          "format": "datetime",
582 591
          "description": "When the video was uploaded."
583 592
        },
584 593
        "aspectRatio": {
585 594
          "ref": "#videoAspectRatio",
586 595
          "type": "ref",
587 596
          "description": "Video aspect ratio."
588 597
        },
589 598
        "showOverlay": {
590 599
          "type": "boolean",
591 600
          "description": "Whether to show the title overlay. Defaults to false."
592 601
        },
593 602
        "videoBlobRef": {
594 603
          "type": "blob",
595 604
          "description": "Reference to the uploaded video blob."
596 605
        }
597 606
      },
598 607
      "description": "A video widget card."
599 608
    },
600 609
    "widgetGithub": {
601 610
      "type": "object",
602 611
      "required": [
603 612
        "id",
604 613
        "type",
605 614
        "githubUsername",
606 615
        "contributions"
607 616
      ],
608 617
      "properties": {
609 618
        "id": {
610 619
          "type": "string",
611 620
          "description": "Unique identifier for the card."
612 621
        },
613 622
        "size": {
614 623
          "type": "string",
615 624
          "description": "Widget display size. Default: 1x1.",
616 625
          "knownValues": [
617 626
            "1x1",
618 627
            "1x2",
619 628
            "2x2"
620 629
          ]
621 630
        },
622 631
        "type": {
623 632
          "type": "string",
624 633
          "const": "github",
625 634
          "description": "Card type discriminator."
626 635
        },
627 636
        "enabled": {
628 637
          "type": "boolean",
629 638
          "description": "Whether the widget is visible on the public page. Defaults to true."
630 639
        },
631 640
        "lastSyncedAt": {
632 641
          "type": "string",
633 642
          "format": "datetime",
634 643
          "description": "When the contribution data was last fetched from GitHub."
635 644
        },
636 645
        "contributions": {
637 646
          "type": "array",
638 647
          "items": {
639 648
            "ref": "#githubContributionDay",
640 649
            "type": "ref"
641 650
          },
642 651
          "description": "Contribution data for the last year."
643 652
        },
644 653
        "githubUsername": {
645 654
          "type": "string",
646 655
          "description": "GitHub username."
647 656
        },
648 657
        "totalContributions": {
649 658
          "type": "integer",
650 659
          "description": "Total number of contributions in the last year."
651 660
        }
652 661
      },
653 662
      "description": "A GitHub contributions graph widget card."
654 663
    },
655 664
    "widgetLastfm": {
656 665
      "type": "object",
657 666
      "required": [
658 667
        "id",
659 668
        "type",
660 669
        "lastfmUsername",
661 670
        "plays"
662 671
      ],
663 672
      "properties": {
664 673
        "id": {
665 674
          "type": "string",
666 675
          "description": "Unique identifier for the card."
667 676
        },
668 677
        "size": {
669 678
          "type": "string",
670 679
          "description": "Widget display size. Default: 1x1.",
671 680
          "knownValues": [
672 681
            "1x1",
673 682
            "1x2",
674 683
            "2x2"
675 684
          ]
676 685
        },
677 686
        "type": {
678 687
          "type": "string",
679 688
          "const": "lastfm",
680 689
          "description": "Card type discriminator."
681 690
        },
682 691
        "plays": {
683 692
          "type": "array",
684 693
          "items": {
685 694
            "ref": "#tealfmPlay",
686 695
            "type": "ref"
687 696
          },
688 697
          "description": "Recently played tracks from Last.fm."
689 698
        },
690 699
        "enabled": {
691 700
          "type": "boolean",
692 701
          "description": "Whether the widget is visible on the public page. Defaults to true."
693 702
        },
694 703
        "lastSyncedAt": {
695 704
          "type": "string",
696 705
          "format": "datetime",
697 706
          "description": "When the play data was last fetched from Last.fm."
698 707
        },
699 708
        "coverArtNotice": {
700 709
          "type": "string",
701 710
          "description": "Copyright notice for cover art images."
702 711
        },
703 712
        "lastfmUsername": {
704 713
          "type": "string",
705 714
          "description": "Last.fm username for fetching scrobbles."
706 715
        }
707 716
      },
708 717
      "description": "A Last.fm recently played tracks widget card."
709 718
    },
710 719
    "widgetMedium": {
711 720
      "type": "object",
712 721
      "required": [
713 722
        "id",
714 723
        "type",
715 724
        "username",
716 725
        "profileUrl"
717 726
      ],
718 727
      "properties": {
719 728
        "id": {
720 729
          "type": "string",
721 730
          "description": "Unique identifier for the card."
722 731
        },
723 732
        "bio": {
724 733
          "type": "string",
725 734
          "description": "Medium bio."
726 735
        },
727 736
        "size": {
728 737
          "type": "string",
729 738
          "description": "Widget display size. Default: 1x1.",
730 739
          "knownValues": [
731 740
            "1x1",
732 741
            "1x2",
733 742
            "2x2",
734 743
            "2x1"
735 744
          ]
736 745
        },
737 746
        "type": {
738 747
          "type": "string",
739 748
          "const": "medium",
740 749
          "description": "Card type discriminator."
741 750
        },
742 751
        "enabled": {
743 752
          "type": "boolean",
744 753
          "description": "Whether the widget is visible on the public page. Defaults to true."
745 754
        },
746 755
        "articles": {
747 756
          "type": "array",
748 757
          "items": {
749 758
            "ref": "#mediumArticle",
750 759
            "type": "ref"
751 760
          },
752 761
          "description": "Recent articles."
753 762
        },
754 763
        "username": {
755 764
          "type": "string",
756 765
          "description": "Medium username."
757 766
        },
758 767
        "profileUrl": {
759 768
          "type": "string",
760 769
          "description": "Medium profile URL."
761 770
        },
762 771
        "displayName": {
763 772
          "type": "string",
764 773
          "description": "Medium display name."
765 774
        },
766 775
        "lastSyncedAt": {
767 776
          "type": "string",
768 777
          "format": "datetime",
769 778
          "description": "When the Medium data was last fetched."
770 779
        },
771 780
        "followerCount": {
772 781
          "type": "integer",
773 782
          "description": "Number of followers."
774 783
        }
775 784
      },
776 785
      "description": "A Medium profile widget card."
777 786
    },
778 787
    "widgetReddit": {
779 788
      "type": "object",
780 789
      "required": [
781 790
        "id",
782 791
        "type",
783 792
        "username",
784 793
        "profileUrl"
785 794
      ],
786 795
      "properties": {
787 796
        "id": {
788 797
          "type": "string",
789 798
          "description": "Unique identifier for the card."
790 799
        },
791 800
        "size": {
792 801
          "type": "string",
793 802
          "description": "Widget display size. Default: 1x1.",
794 803
          "knownValues": [
795 804
            "1x1",
796 805
            "1x2",
797 806
            "2x2",
798 807
            "2x1"
799 808
          ]
800 809
        },
801 810
        "type": {
802 811
          "type": "string",
803 812
          "const": "reddit",
804 813
          "description": "Card type discriminator."
805 814
        },
806 815
        "karma": {
807 816
          "type": "integer",
808 817
          "description": "Total karma."
809 818
        },
810 819
        "enabled": {
811 820
          "type": "boolean",
812 821
          "description": "Whether the widget is visible on the public page. Defaults to true."
813 822
        },
814 823
        "username": {
815 824
          "type": "string",
816 825
          "description": "Reddit username."
817 826
        },
818 827
        "avatarUrl": {
819 828
          "type": "string",
820 829
          "description": "Reddit avatar URL."
821 830
        },
822 831
        "linkKarma": {
823 832
          "type": "integer",
824 833
          "description": "Link karma."
825 834
        },
826 835
        "profileUrl": {
827 836
          "type": "string",
828 837
          "description": "Reddit profile URL."
829 838
        },
830 839
        "recentPosts": {
831 840
          "type": "array",
832 841
          "items": {
833 842
            "ref": "#redditPost",
834 843
            "type": "ref"
835 844
          },
836 845
          "description": "Recent posts."
837 846
        },
838 847
        "commentKarma": {
839 848
          "type": "integer",
840 849
          "description": "Comment karma."
841 850
        },
842 851
        "lastSyncedAt": {
843 852
          "type": "string",
844 853
          "format": "datetime",
845 854
          "description": "When the Reddit data was last fetched."
846 855
        }
847 856
      },
848 857
      "description": "A Reddit profile widget card."
849 858
    },
850 859
    "widgetTealfm": {
851 860
      "type": "object",
852 861
      "required": [
853 862
        "id",
854 863
        "type",
855 864
        "plays"
856 865
      ],
857 866
      "properties": {
858 867
        "id": {
859 868
          "type": "string",
860 869
          "description": "Unique identifier for the card."
861 870
        },
862 871
        "size": {
863 872
          "type": "string",
864 873
          "description": "Widget display size. Default: 1x1.",
865 874
          "knownValues": [
866 875
            "1x1",
867 876
            "1x2",
868 877
            "2x2"
869 878
          ]
870 879
        },
871 880
        "type": {
872 881
          "type": "string",
873 882
          "const": "tealfm",
874 883
          "description": "Card type discriminator."
875 884
        },
876 885
        "plays": {
877 886
          "type": "array",
878 887
          "items": {
879 888
            "ref": "#tealfmPlay",
880 889
            "type": "ref"
881 890
          },
882 891
          "description": "Recently played tracks."
883 892
        },
884 893
        "source": {
885 894
          "type": "string",
886 895
          "description": "Data source for the widget. 'lastfm' for imported Last.fm scrobbles, 'tealfm' (default) for native AT Protocol plays.",
887 896
          "knownValues": [
888 897
            "tealfm",
889 898
            "lastfm"
890 899
          ]
891 900
        },
892 901
        "enabled": {
893 902
          "type": "boolean",
894 903
          "description": "Whether the widget is visible on the public page. Defaults to true."
895 904
        },
896 905
        "lastSyncedAt": {
897 906
          "type": "string",
898 907
          "format": "datetime",
899 908
          "description": "When the play data was last fetched from the PDS."
900 909
        },
901 910
        "coverArtNotice": {
902 911
          "type": "string",
903 912
          "description": "Copyright notice for cover art images."
904 913
        }
905 914
      },
906 915
      "description": "A teal.fm recently played tracks widget card."
907 916
    },
908 917
    "widgetTwitch": {
909 918
      "type": "object",
910 919
      "required": [
911 920
        "id",
912 921
        "type",
913 922
        "username"
914 923
      ],
915 924
      "properties": {
916 925
        "id": {
917 926
          "type": "string",
918 927
          "description": "Unique identifier for the card."
919 928
        },
920 929
        "size": {
921 930
          "type": "string",
922 931
          "description": "Widget display size. Default: 1x1.",
923 932
          "knownValues": [
924 933
            "1x1",
925 934
            "1x2",
926 935
            "2x2",
927 936
            "2x1"
928 937
          ]
929 938
        },
930 939
        "type": {
931 940
          "type": "string",
932 941
          "const": "twitch",
933 942
          "description": "Card type discriminator."
934 943
        },
935 944
        "isLive": {
936 945
          "type": "boolean",
937 946
          "description": "Whether the channel is currently live."
938 947
        },
939 948
        "enabled": {
940 949
          "type": "boolean",
941 950
          "description": "Whether the widget is visible on the public page. Defaults to true."
942 951
        },
943 952
        "username": {
944 953
          "type": "string",
945 954
          "description": "Twitch username."
946 955
        },
947 956
        "streamGame": {
948 957
          "type": "string",
949 958
          "description": "Current stream game/category."
950 959
        },
951 960
        "displayName": {
952 961
          "type": "string",
953 962
          "description": "Twitch display name."
954 963
        },
955 964
        "streamTitle": {
956 965
          "type": "string",
957 966
          "description": "Current stream title."
958 967
        },
959 968
        "viewerCount": {
960 969
          "type": "integer",
961 970
          "description": "Current viewer count."
962 971
        },
963 972
        "lastSyncedAt": {
964 973
          "type": "string",
965 974
          "format": "datetime",
966 975
          "description": "When the channel data was last fetched."
967 976
        },
968 977
        "profileImageUrl": {
969 978
          "type": "string",
970 979
          "description": "Twitch profile image URL."
971 980
        }
972 981
      },
973 982
      "description": "A Twitch channel widget card."
974 983
    },
975 984
    "youtubeVideo": {
976 985
      "type": "object",
977 986
      "required": [
978 987
        "videoId",
979 988
        "title",
980 989
        "publishedAt"
981 990
      ],
982 991
      "properties": {
983 992
        "title": {
984 993
          "type": "string",
985 994
          "description": "Video title."
986 995
        },
987 996
        "isShort": {
988 997
          "type": "boolean",
989 998
          "description": "Whether this is a YouTube Short."
990 999
        },
991 1000
        "videoId": {
992 1001
          "type": "string",
993 1002
          "description": "YouTube video ID."
994 1003
        },
995 1004
        "publishedAt": {
996 1005
          "type": "string",
997 1006
          "description": "When the video was published."
998 1007
        }
999 1008
      },
1000 1009
      "description": "A YouTube video."
1001 1010
    },
1002 1011
    "goodreadsBook": {
1003 1012
      "type": "object",
1004 1013
      "required": [
1005 1014
        "title",
1006 1015
        "author"
1007 1016
      ],
1008 1017
      "properties": {
1009 1018
        "link": {
1010 1019
          "type": "string",
1011 1020
          "description": "URL to the book on Goodreads."
1012 1021
        },
1013 1022
        "title": {
1014 1023
          "type": "string",
1015 1024
          "description": "Book title."
1016 1025
        },
1017 1026
        "author": {
1018 1027
          "type": "string",
1019 1028
          "description": "Book author."
1020 1029
        },
1021 1030
        "rating": {
1022 1031
          "type": "integer",
1023 1032
          "maximum": 5,
1024 1033
          "minimum": 0,
1025 1034
          "description": "User's rating (1-5), or absent if unrated."
1026 1035
        },
1027 1036
        "coverUrl": {
1028 1037
          "type": "string",
1029 1038
          "description": "URL to the book cover image."
1030 1039
        }
1031 1040
      },
1032 1041
      "description": "A book from a Goodreads shelf."
1033 1042
    },
1034 1043
    "mediumArticle": {
1035 1044
      "type": "object",
1036 1045
      "required": [
1037 1046
        "title",
1038 1047
        "url",
1039 1048
        "publishedAt",
1040 1049
        "claps"
1041 1050
      ],
1042 1051
      "properties": {
1043 1052
        "url": {
1044 1053
          "type": "string",
1045 1054
          "description": "Article URL."
1046 1055
        },
1047 1056
        "claps": {
1048 1057
          "type": "integer",
1049 1058
          "description": "Number of claps."
1050 1059
        },
1051 1060
        "title": {
1052 1061
          "type": "string",
1053 1062
          "description": "Article title."
1054 1063
        },
1055 1064
        "subtitle": {
1056 1065
          "type": "string",
1057 1066
          "description": "Article subtitle."
1058 1067
        },
1059 1068
        "publishedAt": {
1060 1069
          "type": "string",
1061 1070
          "description": "When the article was published."
1062 1071
        },
1063 1072
        "readingTime": {
1064 1073
          "type": "integer",
1065 1074
          "description": "Estimated reading time in minutes."
1066 1075
        }
1067 1076
      },
1068 1077
      "description": "A Medium article."
1069 1078
    },
1070 1079
    "widgetBeehiiv": {
1071 1080
      "type": "object",
1072 1081
      "required": [
1073 1082
        "id",
1074 1083
        "type",
1075 1084
        "publicationId",
1076 1085
        "publicationName",
1077 1086
        "publicationUrl"
1078 1087
      ],
1079 1088
      "properties": {
1080 1089
        "id": {
1081 1090
          "type": "string",
1082 1091
          "description": "Unique identifier for the card."
1083 1092
        },
1084 1093
        "size": {
1085 1094
          "type": "string",
1086 1095
          "description": "Widget display size. Default: 1x1.",
1087 1096
          "knownValues": [
1088 1097
            "1x1",
1089 1098
            "1x2",
1090 1099
            "2x2",
1091 1100
            "2x1"
1092 1101
          ]
1093 1102
        },
1094 1103
        "type": {
1095 1104
          "type": "string",
1096 1105
          "const": "beehiiv",
1097 1106
          "description": "Card type discriminator."
1098 1107
        },
1099 1108
        "enabled": {
1100 1109
          "type": "boolean",
1101 1110
          "description": "Whether the widget is visible on the public page. Defaults to true."
1102 1111
        },
1103 1112
        "imageUrl": {
1104 1113
          "type": "string",
1105 1114
          "description": "Publication image URL."
1106 1115
        },
1107 1116
        "description": {
1108 1117
          "type": "string",
1109 1118
          "description": "Publication description."
1110 1119
        },
1111 1120
        "lastSyncedAt": {
1112 1121
          "type": "string",
1113 1122
          "format": "datetime",
1114 1123
          "description": "When the publication data was last fetched."
1115 1124
        },
1116 1125
        "publicationId": {
1117 1126
          "type": "string",
1118 1127
          "description": "Beehiiv publication ID."
1119 1128
        },
1120 1129
        "publicationUrl": {
1121 1130
          "type": "string",
1122 1131
          "description": "URL to the Beehiiv publication."
1123 1132
        },
1124 1133
        "publicationName": {
1125 1134
          "type": "string",
1126 1135
          "description": "Beehiiv publication name."
1127 1136
        }
1128 1137
      },
1129 1138
      "description": "A Beehiiv publication widget card."
1130 1139
    },
1131 1140
    "widgetDiscord": {
1132 1141
      "type": "object",
1133 1142
      "required": [
1134 1143
        "id",
1135 1144
        "type",
1136 1145
        "serverId",
1137 1146
        "serverName",
1138 1147
        "inviteUrl"
1139 1148
      ],
1140 1149
      "properties": {
1141 1150
        "id": {
1142 1151
          "type": "string",
1143 1152
          "description": "Unique identifier for the card."
1144 1153
        },
1145 1154
        "size": {
1146 1155
          "type": "string",
1147 1156
          "description": "Widget display size. Default: 1x1.",
1148 1157
          "knownValues": [
1149 1158
            "1x1",
1150 1159
            "1x2",
1151 1160
            "2x2",
1152 1161
            "2x1"
1153 1162
          ]
1154 1163
        },
1155 1164
        "type": {
1156 1165
          "type": "string",
1157 1166
          "const": "discord",
1158 1167
          "description": "Card type discriminator."
1159 1168
        },
1160 1169
        "enabled": {
1161 1170
          "type": "boolean",
1162 1171
          "description": "Whether the widget is visible on the public page. Defaults to true."
1163 1172
        },
1164 1173
        "iconUrl": {
1165 1174
          "type": "string",
1166 1175
          "description": "Server icon URL."
1167 1176
        },
1168 1177
        "serverId": {
1169 1178
          "type": "string",
1170 1179
          "description": "Discord server ID."
1171 1180
        },
1172 1181
        "bannerUrl": {
1173 1182
          "type": "string",
1174 1183
          "description": "Server banner URL."
1175 1184
        },
1176 1185
        "inviteUrl": {
1177 1186
          "type": "string",
1178 1187
          "description": "Discord invite URL."
1179 1188
        },
1180 1189
        "serverName": {
1181 1190
          "type": "string",
1182 1191
          "description": "Discord server name."
1183 1192
        },
1184 1193
        "description": {
1185 1194
          "type": "string",
1186 1195
          "description": "Server description."
1187 1196
        },
1188 1197
        "memberCount": {
1189 1198
          "type": "integer",
1190 1199
          "description": "Total member count."
1191 1200
        },
1192 1201
        "lastSyncedAt": {
1193 1202
          "type": "string",
1194 1203
          "format": "datetime",
1195 1204
          "description": "When the server data was last fetched."
1196 1205
        },
1197 1206
        "presenceCount": {
1198 1207
          "type": "integer",
1199 1208
          "description": "Online member count."
1200 1209
        }
1201 1210
      },
1202 1211
      "description": "A Discord server widget card."
1203 1212
    },
1204 1213
    "widgetPatreon": {
1205 1214
      "type": "object",
1206 1215
      "required": [
1207 1216
        "id",
1208 1217
        "type",
1209 1218
        "campaignId",
1210 1219
        "creatorName",
1211 1220
        "campaignUrl"
1212 1221
      ],
1213 1222
      "properties": {
1214 1223
        "id": {
1215 1224
          "type": "string",
1216 1225
          "description": "Unique identifier for the card."
1217 1226
        },
1218 1227
        "size": {
1219 1228
          "type": "string",
1220 1229
          "description": "Widget display size. Default: 1x1.",
1221 1230
          "knownValues": [
1222 1231
            "1x1",
1223 1232
            "1x2",
1224 1233
            "2x2",
1225 1234
            "2x1"
1226 1235
          ]
1227 1236
        },
1228 1237
        "type": {
1229 1238
          "type": "string",
1230 1239
          "const": "patreon",
1231 1240
          "description": "Card type discriminator."
1232 1241
        },
1233 1242
        "enabled": {
1234 1243
          "type": "boolean",
1235 1244
          "description": "Whether the widget is visible on the public page. Defaults to true."
1236 1245
        },
1237 1246
        "imageUrl": {
1238 1247
          "type": "string",
1239 1248
          "description": "Campaign image URL."
1240 1249
        },
1241 1250
        "postCount": {
1242 1251
          "type": "integer",
1243 1252
          "description": "Number of posts."
1244 1253
        },
1245 1254
        "campaignId": {
1246 1255
          "type": "string",
1247 1256
          "description": "Patreon campaign ID."
1248 1257
        },
1249 1258
        "campaignUrl": {
1250 1259
          "type": "string",
1251 1260
          "description": "URL to the Patreon campaign."
1252 1261
        },
1253 1262
        "creatorName": {
1254 1263
          "type": "string",
1255 1264
          "description": "Patreon creator name."
1256 1265
        },
1257 1266
        "patronCount": {
1258 1267
          "type": "integer",
1259 1268
          "description": "Number of patrons."
1260 1269
        },
1261 1270
        "creationName": {
1262 1271
          "type": "string",
1263 1272
          "description": "Name of the creation/project."
1264 1273
        },
1265 1274
        "lastSyncedAt": {
1266 1275
          "type": "string",
1267 1276
          "format": "datetime",
1268 1277
          "description": "When the campaign data was last fetched."
1269 1278
        }
1270 1279
      },
1271 1280
      "description": "A Patreon campaign widget card."
1272 1281
    },
1273 1282
    "widgetYoutube": {
1274 1283
      "type": "object",
1275 1284
      "required": [
1276 1285
        "id",
1277 1286
        "type",
1278 1287
        "channelId",
1279 1288
        "channelName",
1280 1289
        "channelUrl"
1281 1290
      ],
1282 1291
      "properties": {
1283 1292
        "id": {
1284 1293
          "type": "string",
1285 1294
          "description": "Unique identifier for the card."
1286 1295
        },
1287 1296
        "size": {
1288 1297
          "type": "string",
1289 1298
          "description": "Widget display size. Default: 1x1.",
1290 1299
          "knownValues": [
1291 1300
            "1x1",
1292 1301
            "1x2",
1293 1302
            "2x2",
1294 1303
            "2x1"
1295 1304
          ]
1296 1305
        },
1297 1306
        "type": {
1298 1307
          "type": "string",
1299 1308
          "const": "youtube",
1300 1309
          "description": "Card type discriminator."
1301 1310
        },
1302 1311
        "enabled": {
1303 1312
          "type": "boolean",
1304 1313
          "description": "Whether the widget is visible on the public page. Defaults to true."
1305 1314
        },
1306 1315
        "channelId": {
1307 1316
          "type": "string",
1308 1317
          "description": "YouTube channel ID."
1309 1318
        },
1310 1319
        "channelUrl": {
1311 1320
          "type": "string",
1312 1321
          "description": "URL to the YouTube channel."
1313 1322
        },
1314 1323
        "channelName": {
1315 1324
          "type": "string",
1316 1325
          "description": "YouTube channel name."
1317 1326
        },
1318 1327
        "latestVideo": {
1319 1328
          "ref": "#youtubeVideo",
1320 1329
          "type": "ref",
1321 1330
          "description": "The latest video from the channel."
1322 1331
        },
1323 1332
        "lastSyncedAt": {
1324 1333
          "type": "string",
1325 1334
          "format": "datetime",
1326 1335
          "description": "When the video data was last fetched."
1327 1336
        }
1328 1337
      },
1329 1338
      "description": "A YouTube channel widget card."
1330 1339
    },
1331 1340
    "widgetSubstack": {
1332 1341
      "type": "object",
1333 1342
      "required": [
1334 1343
        "id",
1335 1344
        "type",
1336 1345
        "publicationId",
1337 1346
        "publicationName",
1338 1347
        "publicationUrl"
1339 1348
      ],
1340 1349
      "properties": {
1341 1350
        "id": {
1342 1351
          "type": "string",
1343 1352
          "description": "Unique identifier for the card."
1344 1353
        },
1345 1354
        "size": {
1346 1355
          "type": "string",
1347 1356
          "description": "Widget display size. Default: 1x1.",
1348 1357
          "knownValues": [
1349 1358
            "1x1",
1350 1359
            "1x2",
1351 1360
            "2x2",
1352 1361
            "2x1"
1353 1362
          ]
1354 1363
        },
1355 1364
        "type": {
1356 1365
          "type": "string",
1357 1366
          "const": "substack",
1358 1367
          "description": "Card type discriminator."
1359 1368
        },
1360 1369
        "enabled": {
1361 1370
          "type": "boolean",
1362 1371
          "description": "Whether the widget is visible on the public page. Defaults to true."
1363 1372
        },
1364 1373
        "imageUrl": {
1365 1374
          "type": "string",
1366 1375
          "description": "Publication image URL."
1367 1376
        },
1368 1377
        "postCount": {
1369 1378
          "type": "integer",
1370 1379
          "description": "Number of posts."
1371 1380
        },
1372 1381
        "authorName": {
1373 1382
          "type": "string",
1374 1383
          "description": "Author name."
1375 1384
        },
1376 1385
        "description": {
1377 1386
          "type": "string",
1378 1387
          "description": "Publication description."
1379 1388
        },
1380 1389
        "lastSyncedAt": {
1381 1390
          "type": "string",
1382 1391
          "format": "datetime",
1383 1392
          "description": "When the publication data was last fetched."
1384 1393
        },
1385 1394
        "coverImageUrl": {
1386 1395
          "type": "string",
1387 1396
          "description": "Publication cover image URL."
1388 1397
        },
1389 1398
        "publicationId": {
1390 1399
          "type": "string",
1391 1400
          "description": "Substack publication ID."
1392 1401
        },
1393 1402
        "publicationUrl": {
1394 1403
          "type": "string",
1395 1404
          "description": "URL to the Substack publication."
1396 1405
        },
1397 1406
        "publicationName": {
1398 1407
          "type": "string",
1399 1408
          "description": "Substack publication name."
1400 1409
        },
1401 1410
        "subscriberCount": {
1402 1411
          "type": "integer",
1403 1412
          "description": "Number of subscribers."
1404 1413
        }
1405 1414
      },
1406 1415
      "description": "A Substack publication widget card."
1407 1416
    },
1408 1417
    "widgetGoodreads": {
1409 1418
      "type": "object",
1410 1419
      "required": [
1411 1420
        "id",
1412 1421
        "type",
1413 1422
        "goodreadsUserId",
1414 1423
        "shelf",
1415 1424
        "books"
1416 1425
      ],
1417 1426
      "properties": {
1418 1427
        "id": {
1419 1428
          "type": "string",
1420 1429
          "description": "Unique identifier for the card."
1421 1430
        },
1422 1431
        "size": {
1423 1432
          "type": "string",
1424 1433
          "description": "Widget display size. Default: 1x1.",
1425 1434
          "knownValues": [
1426 1435
            "1x1",
1427 1436
            "1x2",
1428 1437
            "2x2"
1429 1438
          ]
1430 1439
        },
1431 1440
        "type": {
1432 1441
          "type": "string",
1433 1442
          "const": "goodreads",
1434 1443
          "description": "Card type discriminator."
1435 1444
        },
1436 1445
        "books": {
1437 1446
          "type": "array",
1438 1447
          "items": {
1439 1448
            "ref": "#goodreadsBook",
1440 1449
            "type": "ref"
1441 1450
          },
1442 1451
          "description": "Books on the currently-reading shelf."
1443 1452
        },
1444 1453
        "shelf": {
1445 1454
          "type": "string",
1446 1455
          "description": "Shelf name (currently-reading, read, or to-read)."
1447 1456
        },
1448 1457
        "enabled": {
1449 1458
          "type": "boolean",
1450 1459
          "description": "Whether the widget is visible on the public page. Defaults to true."
1451 1460
        },
1452 1461
        "readBooks": {
1453 1462
          "type": "array",
1454 1463
          "items": {
1455 1464
            "ref": "#goodreadsBook",
1456 1465
            "type": "ref"
1457 1466
          },
1458 1467
          "description": "Books on the read shelf (used by 1x2 size)."
1459 1468
        },
1460 1469
        "lastSyncedAt": {
1461 1470
          "type": "string",
1462 1471
          "format": "datetime",
1463 1472
          "description": "When the book data was last fetched from Goodreads."
1464 1473
        },
1465 1474
        "goodreadsUserId": {
1466 1475
          "type": "string",
1467 1476
          "description": "Goodreads numeric user ID."
1468 1477
        }
1469 1478
      },
1470 1479
      "description": "A Goodreads bookshelf widget card."
1471 1480
    },
1472 1481
    "videoAspectRatio": {
1473 1482
      "type": "object",
1474 1483
      "required": [
1475 1484
        "width",
1476 1485
        "height"
1477 1486
      ],
1478 1487
      "properties": {
1479 1488
        "width": {
1480 1489
          "type": "integer",
1481 1490
          "description": "Video width."
1482 1491
        },
1483 1492
        "height": {
1484 1493
          "type": "integer",
1485 1494
          "description": "Video height."
1486 1495
        }
1487 1496
      },
1488 1497
      "description": "Video aspect ratio dimensions."
1489 1498
    },
1490 1499
    "connectedServices": {
1491 1500
      "type": "object",
1492 1501
      "properties": {
1493 1502
        "githubUsername": {
1494 1503
          "type": "string",
1495 1504
          "description": "GitHub username."
1496 1505
        },
1497 1506
        "lastfmUsername": {
1498 1507
          "type": "string",
1499 1508
          "description": "Last.fm username."
1500 1509
        },
1501 1510
        "goodreadsUserId": {
1502 1511
          "type": "string",
1503 1512
          "description": "Goodreads numeric user ID."
1504 1513
        }
1505 1514
      },
1506 1515
      "description": "Saved service credentials/identifiers for pre-filling widget forms."
1507 1516
    },
1508 1517
    "githubContributionDay": {
1509 1518
      "type": "object",
1510 1519
      "required": [
1511 1520
        "date",
1512 1521
        "count",
1513 1522
        "level"
1514 1523
      ],
1515 1524
      "properties": {
1516 1525
        "date": {
1517 1526
          "type": "string",
1518 1527
          "description": "Date in YYYY-MM-DD format."
1519 1528
        },
1520 1529
        "count": {
1521 1530
          "type": "integer",
1522 1531
          "description": "Number of contributions on this day."
1523 1532
        },
1524 1533
        "level": {
1525 1534
          "type": "integer",
1526 1535
          "maximum": 4,
1527 1536
          "minimum": 0,
1528 1537
          "description": "Contribution intensity level (0=none, 4=highest)."
1529 1538
        }
1530 1539
      },
1531 1540
      "description": "A single day's contribution data from GitHub."
1532 1541
    }
1533 1542
  },
1534 1543
  "$type": "com.atproto.lexicon.schema",
1535 1544
  "lexicon": 1
1536 1545
}

Compare Other Versions

Lexicon Garden

@