removed/moved test output

Orual 4b2c8898 593dbcb3

+2 -1280
-1278
test-output/test-sidenotes.html
··· 1 - <!DOCTYPE html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="utf-8"> 5 - <meta name="viewport" content="width=device-width, initial-scale=1"> 6 - <title>test-sidenotes</title> 7 - <style> 8 - /* CSS Reset */ 9 - *, *::before, *::after { 10 - box-sizing: border-box; 11 - margin: 0; 12 - padding: 0; 13 - } 14 - 15 - /* CSS Variables - Light Mode (default) */ 16 - :root { 17 - --color-base: #faf4ed; 18 - --color-surface: #fffaf3; 19 - --color-overlay: #f2e9e1; 20 - --color-text: #1f1d2e; 21 - --color-muted: #635e74; 22 - --color-subtle: #4a4560; 23 - --color-emphasis: #1e1a2d; 24 - --color-primary: #907aa9; 25 - --color-secondary: #56949f; 26 - --color-tertiary: #286983; 27 - --color-error: #b4637a; 28 - --color-warning: #ea9d34; 29 - --color-success: #286983; 30 - --color-border: #dfdad9; 31 - --color-link: #d7827e; 32 - --color-highlight: #cecacd; 33 - 34 - --font-body: 'Adobe Caslon Pro','Latin Modern Roman','Times New Roman','serif'; 35 - --font-heading: 'IBM Plex Sans','system-ui','sans-serif'; 36 - --font-mono: 'Ioskeley Mono','IBM Plex Mono','Berkeley Mono','Consolas','monospace'; 37 - 38 - --spacing-base: 16px; 39 - --spacing-line-height: 1.6; 40 - --spacing-scale: 1.25; 41 - } 42 - 43 - /* CSS Variables - Dark Mode */ 44 - @media (prefers-color-scheme: dark) { 45 - :root { 46 - --color-base: #191724; 47 - --color-surface: #1f1d2e; 48 - --color-overlay: #26233a; 49 - --color-text: #e0def4; 50 - --color-muted: #6e6a86; 51 - --color-subtle: #908caa; 52 - --color-emphasis: #e0def4; 53 - --color-primary: #c4a7e7; 54 - --color-secondary: #9ccfd8; 55 - --color-tertiary: #ebbcba; 56 - --color-error: #eb6f92; 57 - --color-warning: #f6c177; 58 - --color-success: #31748f; 59 - --color-border: #403d52; 60 - --color-link: #ebbcba; 61 - --color-highlight: #524f67; 62 - } 63 - } 64 - 65 - /* Base Styles */ 66 - html { 67 - font-size: var(--spacing-base); 68 - line-height: var(--spacing-line-height); 69 - } 70 - 71 - /* Scoped to notebook-content container */ 72 - .notebook-content { 73 - font-family: var(--font-body); 74 - color: var(--color-text); 75 - background-color: var(--color-base); 76 - margin: 0 auto; 77 - padding: 1rem 0rem; 78 - word-wrap: break-word; 79 - overflow-wrap: break-word; 80 - counter-reset: sidenote-counter; 81 - max-width: 95ch; 82 - } 83 - 84 - /* When sidenotes exist, body padding creates the gutter */ 85 - /* Left padding shrinks first as viewport narrows, right stays for sidenotes */ 86 - body:has(.sidenote) { 87 - padding-left: clamp(0rem, calc((100vw - 95ch - 15.5rem - 2rem) / 2), 15.5rem); 88 - padding-right: 15.5rem; 89 - } 90 - 91 - /* Typography */ 92 - h1, h2, h3, h4, h5, h6 { 93 - font-family: var(--font-heading); 94 - margin-top: calc(1rem * var(--spacing-scale)); 95 - margin-bottom: 0.5rem; 96 - line-height: 1.2; 97 - } 98 - 99 - h1 { 100 - font-size: 2rem; 101 - color: var(--color-secondary); 102 - } 103 - h2 { 104 - font-size: 1.5rem; 105 - color: var(--color-primary); 106 - } 107 - h3 { 108 - font-size: 1.25rem; 109 - color: var(--color-secondary); 110 - } 111 - h4 { 112 - font-size: 1.2rem; 113 - color: var(--color-tertiary); 114 - } 115 - h5 { 116 - font-size: 1.125rem; 117 - color: var(--color-secondary); 118 - } 119 - h6 { font-size: 1rem; } 120 - 121 - p { 122 - margin-bottom: 1rem; 123 - word-wrap: break-word; 124 - overflow-wrap: break-word; 125 - } 126 - 127 - a { 128 - color: var(--color-link); 129 - text-decoration: none; 130 - } 131 - 132 - .notebook-content a:hover { 133 - color: var(--color-emphasis); 134 - text-decoration: underline; 135 - } 136 - 137 - /* Wikilink validation (editor) */ 138 - .link-valid { 139 - color: var(--color-link); 140 - } 141 - 142 - .link-broken { 143 - color: var(--color-error); 144 - text-decoration: underline wavy; 145 - text-decoration-color: var(--color-error); 146 - opacity: 0.8; 147 - } 148 - 149 - /* Selection */ 150 - ::selection { 151 - background: var(--color-highlight); 152 - color: var(--color-text); 153 - } 154 - 155 - /* Lists */ 156 - ul, ol { 157 - margin-left: 1rem; 158 - margin-bottom: 1rem; 159 - } 160 - 161 - li { 162 - margin-bottom: 0.25rem; 163 - } 164 - 165 - /* Code */ 166 - code { 167 - font-family: var(--font-mono); 168 - background: var(--color-surface); 169 - padding: 0.125rem 0.25rem; 170 - border-radius: 4px; 171 - font-size: 0.9em; 172 - } 173 - 174 - pre { 175 - overflow-x: auto; 176 - margin-bottom: 1rem; 177 - border-radius: 5px; 178 - border: 1px solid var(--color-border); 179 - box-sizing: border-box; 180 - } 181 - 182 - /* Code blocks inside pre are handled by syntax theme */ 183 - pre code { 184 - 185 - display: block; 186 - width: fit-content; 187 - min-width: 100%; 188 - padding: 1rem; 189 - background: var(--color-surface); 190 - } 191 - 192 - /* Math */ 193 - .math { 194 - font-family: var(--font-mono); 195 - } 196 - 197 - .math-display { 198 - display: block; 199 - margin: 1rem 0; 200 - text-align: center; 201 - } 202 - 203 - /* Blockquotes */ 204 - blockquote { 205 - border-left: 2px solid var(--color-secondary); 206 - background: var(--color-surface); 207 - padding-left: 1rem; 208 - padding-right: 1rem; 209 - padding-top: 0.5rem; 210 - padding-bottom: 0.04rem; 211 - margin: 1rem 0; 212 - font-size: 0.95em; 213 - border-bottom-right-radius: 5px; 214 - border-top-right-radius: 5px; 215 - } 216 - } 217 - 218 - /* Tables */ 219 - table { 220 - border-collapse: collapse; 221 - width: 100%; 222 - margin-bottom: 1rem; 223 - display: block; 224 - overflow-x: auto; 225 - max-width: 100%; 226 - } 227 - 228 - th, td { 229 - border: 1px solid var(--color-border); 230 - padding: 0.5rem; 231 - text-align: left; 232 - } 233 - 234 - th { 235 - background: var(--color-surface); 236 - font-weight: 600; 237 - } 238 - 239 - tr:hover { 240 - background: var(--color-surface); 241 - } 242 - 243 - /* Footnotes */ 244 - .footnote-reference { 245 - font-size: 0.8em; 246 - color: var(--color-subtle); 247 - } 248 - 249 - .footnote-definition { 250 - order: 9999; 251 - margin: 0; 252 - padding: 0.5rem 0; 253 - font-size: 0.9em; 254 - } 255 - 256 - .footnote-definition:first-of-type { 257 - margin-top: 2rem; 258 - padding-top: 1rem; 259 - border-top: 2px solid var(--color-border); 260 - } 261 - 262 - .footnote-definition:first-of-type::before { 263 - content: "Footnotes"; 264 - display: block; 265 - font-weight: 600; 266 - font-size: 1.1em; 267 - color: var(--color-subtle); 268 - margin-bottom: 0.75rem; 269 - } 270 - 271 - .footnote-definition-label { 272 - font-weight: 600; 273 - margin-right: 0.5rem; 274 - color: var(--color-primary); 275 - } 276 - 277 - /* Aside blocks (via WeaverBlock prefix) */ 278 - aside, .aside { 279 - float: left; 280 - width: 40%; 281 - margin: 0 1.5rem 1rem 0; 282 - padding: 1rem; 283 - background: var(--color-surface); 284 - border-right: 3px solid var(--color-primary); 285 - font-size: 0.9em; 286 - clear: left; 287 - } 288 - 289 - aside > *:first-child, 290 - .aside > *:first-child { 291 - margin-top: 0; 292 - } 293 - 294 - aside > *:last-child, 295 - .aside > *:last-child { 296 - margin-bottom: 0; 297 - } 298 - 299 - /* Reset blockquote styling inside asides */ 300 - aside > blockquote, 301 - .aside > blockquote { 302 - border-left: none; 303 - background: transparent; 304 - padding: 0; 305 - margin: 0; 306 - font-size: inherit; 307 - } 308 - 309 - /* Indent utilities */ 310 - .indent-1 { margin-left: 1em; } 311 - .indent-2 { margin-left: 2em; } 312 - .indent-3 { margin-left: 3em; } 313 - 314 - /* Tufte-style Sidenotes */ 315 - /* Hide checkbox for sidenote toggle */ 316 - .margin-toggle { 317 - display: none; 318 - } 319 - 320 - /* Sidenote number marker (inline superscript) */ 321 - .sidenote-number { 322 - counter-increment: sidenote-counter; 323 - } 324 - 325 - .sidenote-number::after { 326 - content: counter(sidenote-counter); 327 - font-size: 0.7em; 328 - position: relative; 329 - top: -0.5em; 330 - color: var(--color-primary); 331 - padding-left: 0.1em; 332 - } 333 - 334 - /* Sidenote content (margin notes on wide screens) */ 335 - .sidenote { 336 - float: right; 337 - clear: right; 338 - margin-right: -15.5rem; 339 - width: 14rem; 340 - margin-top: 0.3rem; 341 - margin-bottom: 1rem; 342 - font-size: 0.85em; 343 - line-height: 1.4; 344 - color: var(--color-subtle); 345 - } 346 - 347 - .sidenote::before { 348 - content: counter(sidenote-counter) ". "; 349 - color: var(--color-primary); 350 - } 351 - 352 - /* Mobile sidenotes: toggle behavior */ 353 - @media (max-width: 900px) { 354 - /* Reset sidenote gutter on mobile */ 355 - body:has(.sidenote) { 356 - padding-right: 0; 357 - } 358 - 359 - aside, .aside { 360 - float: none; 361 - width: 100%; 362 - margin: 1rem 0; 363 - } 364 - 365 - .sidenote { 366 - display: none; 367 - } 368 - 369 - .margin-toggle:checked + .sidenote { 370 - display: block; 371 - float: none; 372 - width: 95%; 373 - margin: 0.5rem 2.5%; 374 - padding: 0.5rem; 375 - background: var(--color-surface); 376 - border-left: 2px solid var(--color-primary); 377 - } 378 - 379 - label.sidenote-number { 380 - cursor: pointer; 381 - } 382 - 383 - label.sidenote-number::after { 384 - text-decoration: underline; 385 - } 386 - } 387 - 388 - /* Images */ 389 - img { 390 - max-width: 100%; 391 - height: auto; 392 - display: block; 393 - margin: 1rem 0; 394 - border-radius: 4px; 395 - } 396 - 397 - /* Hygiene for iframes */ 398 - .html-embed-block { 399 - max-width: 100%; 400 - height: auto; 401 - display: block; 402 - margin: 1rem 0; 403 - } 404 - 405 - /* AT Protocol Embeds - Container */ 406 - /* Light mode: paper with shadow, dark mode: blueprint with borders */ 407 - .atproto-embed { 408 - display: block; 409 - position: relative; 410 - max-width: 550px; 411 - margin: 1rem 0; 412 - padding: 1rem; 413 - background: var(--color-surface); 414 - border-left: 2px solid var(--color-secondary); 415 - box-shadow: 0 1px 2px color-mix(in srgb, var(--color-text) 8%, transparent); 416 - } 417 - 418 - .atproto-embed:hover { 419 - border-left-color: var(--color-primary); 420 - } 421 - 422 - @media (prefers-color-scheme: dark) { 423 - .atproto-embed { 424 - box-shadow: none; 425 - border: 1px solid var(--color-border); 426 - border-left: 2px solid var(--color-secondary); 427 - } 428 - } 429 - 430 - .atproto-embed-placeholder { 431 - color: var(--color-muted); 432 - font-style: italic; 433 - } 434 - 435 - .embed-loading { 436 - display: block; 437 - padding: 0.5rem 0; 438 - color: var(--color-subtle); 439 - font-family: var(--font-mono); 440 - font-size: 0.85rem; 441 - } 442 - 443 - /* Embed Author Block */ 444 - .embed-author { 445 - display: flex; 446 - align-items: center; 447 - gap: 0.75rem; 448 - padding-bottom: 0.5rem; 449 - } 450 - 451 - .embed-avatar { 452 - width: 36px; 453 - height: 36px; 454 - max-width: 36px; 455 - max-height: 36px; 456 - aspect-ratio: 1; 457 - margin: 0; 458 - object-fit: cover; 459 - } 460 - 461 - .embed-author-info { 462 - display: flex; 463 - flex-direction: column; 464 - gap: 0; 465 - min-width: 0; 466 - } 467 - 468 - .embed-avatar-link { 469 - display: block; 470 - flex-shrink: 0; 471 - } 472 - 473 - .embed-author-name { 474 - font-weight: 600; 475 - color: var(--color-text); 476 - overflow: hidden; 477 - text-overflow: ellipsis; 478 - white-space: nowrap; 479 - text-decoration: none; 480 - line-height: 1.2; 481 - } 482 - 483 - a.embed-author-name:hover { 484 - color: var(--color-link); 485 - } 486 - 487 - .embed-author-handle { 488 - font-size: 0.85em; 489 - font-family: var(--font-mono); 490 - color: var(--color-subtle); 491 - text-decoration: none; 492 - overflow: hidden; 493 - text-overflow: ellipsis; 494 - white-space: nowrap; 495 - line-height: 1.2; 496 - } 497 - 498 - .embed-author-handle:hover { 499 - color: var(--color-link); 500 - } 501 - 502 - /* Card-wide clickable link (sits behind content) */ 503 - .embed-card-link { 504 - position: absolute; 505 - inset: 0; 506 - z-index: 0; 507 - } 508 - 509 - .embed-card-link:focus { 510 - outline: 2px solid var(--color-primary); 511 - outline-offset: 2px; 512 - } 513 - 514 - /* Interactive elements sit above the card link */ 515 - .embed-author, 516 - .embed-external, 517 - .embed-quote, 518 - .embed-images, 519 - .embed-meta { 520 - position: relative; 521 - z-index: 1; 522 - } 523 - 524 - /* Embed Content Block */ 525 - .embed-content { 526 - display: block; 527 - color: var(--color-text); 528 - line-height: 1.5; 529 - margin-bottom: 0.75rem; 530 - white-space: pre-wrap; 531 - } 532 - 533 - 534 - 535 - .embed-description { 536 - display: block; 537 - color: var(--color-text); 538 - font-size: 0.95em; 539 - line-height: 1.4; 540 - } 541 - 542 - /* Embed Metadata Block */ 543 - .embed-meta { 544 - display: flex; 545 - justify-content: space-between; 546 - align-items: center; 547 - font-size: 0.85em; 548 - color: var(--color-muted); 549 - margin-top: 0.75rem; 550 - } 551 - 552 - .embed-stats { 553 - display: flex; 554 - gap: 1rem; 555 - font-family: var(--font-mono); 556 - } 557 - 558 - .embed-stat { 559 - color: var(--color-subtle); 560 - font-size: 0.9em; 561 - } 562 - 563 - .embed-time { 564 - color: var(--color-subtle); 565 - text-decoration: none; 566 - font-family: var(--font-mono); 567 - font-size: 0.9em; 568 - } 569 - 570 - .embed-time:hover { 571 - color: var(--color-link); 572 - } 573 - 574 - .embed-type { 575 - font-size: 0.8em; 576 - color: var(--color-subtle); 577 - font-family: var(--font-mono); 578 - text-transform: uppercase; 579 - letter-spacing: 0.05em; 580 - } 581 - 582 - /* Embed URL link (shown with syntax in editor) */ 583 - .embed-url { 584 - color: var(--color-link); 585 - font-family: var(--font-mono); 586 - font-size: 0.9em; 587 - word-break: break-all; 588 - } 589 - 590 - /* External link cards */ 591 - .embed-external { 592 - display: flex; 593 - gap: 0.75rem; 594 - padding: 0.75rem; 595 - background: var(--color-surface); 596 - border: 1px dashed var(--color-border); 597 - text-decoration: none; 598 - color: inherit; 599 - margin-top: 0.5rem; 600 - } 601 - 602 - .embed-external:hover { 603 - border-left: 2px solid var(--color-primary); 604 - margin-left: -1px; 605 - } 606 - 607 - @media (prefers-color-scheme: dark) { 608 - .embed-external { 609 - border: 1px solid var(--color-border); 610 - } 611 - 612 - .embed-external:hover { 613 - border-left: 2px solid var(--color-primary); 614 - margin-left: -1px; 615 - } 616 - } 617 - 618 - .embed-external-thumb { 619 - width: 120px; 620 - height: 80px; 621 - object-fit: cover; 622 - flex-shrink: 0; 623 - } 624 - 625 - .embed-external-info { 626 - display: flex; 627 - flex-direction: column; 628 - gap: 0.25rem; 629 - min-width: 0; 630 - } 631 - 632 - .embed-external-title { 633 - font-weight: 600; 634 - color: var(--color-text); 635 - overflow: hidden; 636 - text-overflow: ellipsis; 637 - white-space: nowrap; 638 - } 639 - 640 - .embed-external-description { 641 - font-size: 0.9em; 642 - color: var(--color-muted); 643 - overflow: hidden; 644 - text-overflow: ellipsis; 645 - display: -webkit-box; 646 - -webkit-line-clamp: 2; 647 - -webkit-box-orient: vertical; 648 - } 649 - 650 - .embed-external-url { 651 - font-size: 0.8em; 652 - font-family: var(--font-mono); 653 - color: var(--color-subtle); 654 - } 655 - 656 - /* Image embeds */ 657 - .embed-images { 658 - display: grid; 659 - gap: 4px; 660 - margin-top: 0.5rem; 661 - overflow: hidden; 662 - } 663 - 664 - .embed-images-1 { 665 - grid-template-columns: 1fr; 666 - } 667 - 668 - .embed-images-2 { 669 - grid-template-columns: 1fr 1fr; 670 - } 671 - 672 - .embed-images-3 { 673 - grid-template-columns: 1fr 1fr; 674 - } 675 - 676 - .embed-images-4 { 677 - grid-template-columns: 1fr 1fr; 678 - } 679 - 680 - .embed-image-link { 681 - display: block; 682 - line-height: 0; 683 - } 684 - 685 - .embed-image { 686 - width: 100%; 687 - height: auto; 688 - max-height: 500px; 689 - object-fit: cover; 690 - object-position: center; 691 - margin: 0; 692 - } 693 - 694 - /* Quoted records */ 695 - .embed-quote { 696 - display: block; 697 - margin-top: 0.5rem; 698 - padding: 0.75rem; 699 - background: var(--color-overlay); 700 - border-left: 2px solid var(--color-tertiary); 701 - } 702 - 703 - @media (prefers-color-scheme: dark) { 704 - .embed-quote { 705 - border: 1px solid var(--color-border); 706 - border-left: 2px solid var(--color-tertiary); 707 - } 708 - } 709 - 710 - .embed-quote .embed-author { 711 - margin-bottom: 0.5rem; 712 - } 713 - 714 - .embed-quote .embed-avatar { 715 - width: 24px; 716 - height: 24px; 717 - min-width: 24px; 718 - min-height: 24px; 719 - max-width: 24px; 720 - max-height: 24px; 721 - } 722 - 723 - .embed-quote .embed-content { 724 - font-size: 0.95em; 725 - margin-bottom: 0; 726 - } 727 - 728 - /* Placeholder states */ 729 - .embed-video-placeholder, 730 - .embed-not-found, 731 - .embed-blocked, 732 - .embed-detached, 733 - .embed-unknown { 734 - display: block; 735 - padding: 1rem; 736 - background: var(--color-overlay); 737 - border-left: 2px solid var(--color-border); 738 - color: var(--color-muted); 739 - font-style: italic; 740 - margin-top: 0.5rem; 741 - font-family: var(--font-mono); 742 - font-size: 0.9em; 743 - } 744 - 745 - @media (prefers-color-scheme: dark) { 746 - .embed-video-placeholder, 747 - .embed-not-found, 748 - .embed-blocked, 749 - .embed-detached, 750 - .embed-unknown { 751 - border: 1px dashed var(--color-border); 752 - } 753 - } 754 - 755 - /* Record card embeds (feeds, lists, labelers, starter packs) */ 756 - .embed-record-card { 757 - display: block; 758 - margin-top: 0.5rem; 759 - padding: 0.75rem; 760 - background: var(--color-overlay); 761 - border-left: 2px solid var(--color-tertiary); 762 - } 763 - 764 - .embed-record-card > .embed-author-name { 765 - display: block; 766 - font-size: 1.1em; 767 - } 768 - 769 - .embed-subtitle { 770 - display: block; 771 - font-size: 0.85em; 772 - color: var(--color-muted); 773 - margin-bottom: 0.5rem; 774 - } 775 - 776 - .embed-record-card .embed-description { 777 - display: block; 778 - margin: 0.5rem 0; 779 - } 780 - 781 - .embed-record-card .embed-stats { 782 - display: block; 783 - margin-top: 0.25rem; 784 - } 785 - 786 - /* Generic record fields */ 787 - .embed-fields { 788 - display: block; 789 - margin-top: 0.5rem; 790 - font-family: var(--font-ui); 791 - font-size: 0.85rem; 792 - color: var(--color-muted); 793 - } 794 - 795 - .embed-field { 796 - display: block; 797 - margin-top: 0.25rem; 798 - } 799 - 800 - /* Nested fields get indentation */ 801 - .embed-fields .embed-fields { 802 - display: block; 803 - margin-top: 0.5rem; 804 - margin-left: 1rem; 805 - padding-left: 0.5rem; 806 - border-left: 1px solid var(--color-border); 807 - } 808 - 809 - /* Type label inside fields should be block with spacing */ 810 - .embed-fields > .embed-author-handle { 811 - display: block; 812 - margin-bottom: 0.25rem; 813 - } 814 - 815 - .embed-field-name { 816 - color: var(--color-subtle); 817 - } 818 - 819 - .embed-field-number { 820 - color: var(--color-tertiary); 821 - } 822 - 823 - .embed-field-date { 824 - color: var(--color-muted); 825 - } 826 - 827 - .embed-field-count { 828 - color: var(--color-muted); 829 - font-style: italic; 830 - } 831 - 832 - .embed-field-bool-true { 833 - color: var(--color-success); 834 - } 835 - 836 - .embed-field-bool-false { 837 - color: var(--color-muted); 838 - } 839 - 840 - .embed-field-link, 841 - .embed-field-aturi { 842 - color: var(--color-link); 843 - text-decoration: none; 844 - } 845 - 846 - .embed-field-link:hover, 847 - .embed-field-aturi:hover { 848 - text-decoration: underline; 849 - } 850 - 851 - .embed-field-did { 852 - font-family: var(--font-mono); 853 - font-size: 0.9em; 854 - } 855 - 856 - .embed-field-did .did-scheme, 857 - .embed-field-did .did-separator { 858 - color: var(--color-muted); 859 - } 860 - 861 - .embed-field-did .did-method { 862 - color: var(--color-tertiary); 863 - } 864 - 865 - .embed-field-did .did-identifier { 866 - color: var(--color-text); 867 - } 868 - 869 - .embed-field-nsid { 870 - color: var(--color-secondary); 871 - } 872 - 873 - .embed-field-handle { 874 - color: var(--color-link); 875 - } 876 - 877 - /* AT URI highlighting */ 878 - .aturi-scheme { 879 - color: var(--color-muted); 880 - } 881 - 882 - .aturi-slash { 883 - color: var(--color-muted); 884 - } 885 - 886 - .aturi-authority { 887 - color: var(--color-link); 888 - } 889 - 890 - .aturi-collection { 891 - color: var(--color-secondary); 892 - } 893 - 894 - .aturi-rkey { 895 - color: var(--color-tertiary); 896 - } 897 - 898 - /* Generic AT Protocol record embed */ 899 - .atproto-record > .embed-author-handle { 900 - display: block; 901 - margin-bottom: 0.25rem; 902 - } 903 - 904 - .atproto-record > .embed-author-name { 905 - display: block; 906 - margin-bottom: 0.5rem; 907 - } 908 - 909 - .atproto-record > .embed-content { 910 - margin-bottom: 0.5rem; 911 - } 912 - 913 - /* Notebook entry embed - full width, expandable */ 914 - .atproto-entry { 915 - max-width: none; 916 - width: 100%; 917 - margin: 1.5rem 0; 918 - padding: 0; 919 - background: var(--color-surface); 920 - border: 1px solid var(--color-border); 921 - border-left: 1px solid var(--color-border); 922 - box-shadow: none; 923 - overflow: hidden; 924 - } 925 - 926 - .atproto-entry:hover { 927 - border-left-color: var(--color-border); 928 - } 929 - 930 - @media (prefers-color-scheme: dark) { 931 - .atproto-entry { 932 - border: 1px solid var(--color-border); 933 - border-left: 1px solid var(--color-border); 934 - } 935 - } 936 - 937 - .embed-entry-header { 938 - display: flex; 939 - flex-wrap: wrap; 940 - align-items: baseline; 941 - gap: 0.5rem 1rem; 942 - padding: 0.75rem 1rem; 943 - background: var(--color-overlay); 944 - border-bottom: 1px solid var(--color-border); 945 - } 946 - 947 - .embed-entry-title { 948 - font-size: 1.1em; 949 - font-weight: 600; 950 - color: var(--color-text); 951 - } 952 - 953 - .embed-entry-author { 954 - font-size: 0.85em; 955 - color: var(--color-muted); 956 - } 957 - 958 - /* Hidden checkbox for expand/collapse */ 959 - .embed-entry-toggle { 960 - display: none; 961 - } 962 - 963 - /* Content wrapper - scrollable when collapsed */ 964 - .embed-entry-content { 965 - max-height: 30rem; 966 - overflow-y: auto; 967 - padding: 1rem; 968 - transition: max-height 0.3s ease; 969 - } 970 - 971 - /* When checkbox is checked, expand fully */ 972 - .embed-entry-toggle:checked ~ .embed-entry-content { 973 - max-height: none; 974 - } 975 - 976 - /* Expand/collapse button */ 977 - .embed-entry-expand { 978 - display: block; 979 - width: 100%; 980 - padding: 0.5rem; 981 - text-align: center; 982 - font-size: 0.85em; 983 - font-family: var(--font-ui); 984 - color: var(--color-muted); 985 - background: var(--color-overlay); 986 - border-top: 1px solid var(--color-border); 987 - cursor: pointer; 988 - user-select: none; 989 - } 990 - 991 - .embed-entry-expand:hover { 992 - color: var(--color-text); 993 - background: var(--color-surface); 994 - } 995 - 996 - /* Toggle button text */ 997 - .embed-entry-expand::before { 998 - content: "Expand ↓"; 999 - } 1000 - 1001 - .embed-entry-toggle:checked ~ .embed-entry-expand::before { 1002 - content: "Collapse ↑"; 1003 - } 1004 - 1005 - /* Hide expand button if content doesn't overflow (via JS class) */ 1006 - .atproto-entry.no-overflow .embed-entry-expand { 1007 - display: none; 1008 - } 1009 - 1010 - /* Horizontal Rule */ 1011 - hr { 1012 - border: none; 1013 - border-top: 2px solid var(--color-border); 1014 - margin: 2rem 0; 1015 - } 1016 - 1017 - /* Tablet and mobile responsiveness */ 1018 - @media (max-width: 900px) { 1019 - .notebook-content { 1020 - padding: 1.5rem 1rem; 1021 - max-width: 100%; 1022 - } 1023 - 1024 - h1 { font-size: 1.85rem; } 1025 - h2 { font-size: 1.4rem; } 1026 - h3 { font-size: 1.2rem; } 1027 - 1028 - blockquote { 1029 - margin-left: 0; 1030 - margin-right: 0; 1031 - } 1032 - } 1033 - 1034 - /* Small mobile phones */ 1035 - @media (max-width: 480px) { 1036 - .notebook-content { 1037 - padding: 1rem 0.75rem; 1038 - } 1039 - 1040 - h1 { font-size: 1.65rem; } 1041 - h2 { font-size: 1.3rem; } 1042 - h3 { font-size: 1.1rem; } 1043 - 1044 - blockquote { 1045 - padding-left: 0.75rem; 1046 - padding-right: 0.75rem; 1047 - } 1048 - } 1049 - </style> 1050 - <style> 1051 - /* Syntax highlighting - Light Mode (default) */ 1052 - /* 1053 - * theme "Rosé Pine Dawn" generated by syntect 1054 - */ 1055 - 1056 - .wvc-code { 1057 - color: #575279; 1058 - background-color: #faf4ed; 1059 - } 1060 - 1061 - .wvc-comment { 1062 - color: #797593; 1063 - font-style: italic; 1064 - } 1065 - .wvc-string, .wvc-punctuation.wvc-definition.wvc-string { 1066 - color: #ea9d34; 1067 - } 1068 - .wvc-constant.wvc-numeric { 1069 - color: #ea9d34; 1070 - } 1071 - .wvc-constant.wvc-language { 1072 - color: #ea9d34; 1073 - font-weight: bold; 1074 - } 1075 - .wvc-constant.wvc-character, .wvc-constant.wvc-other { 1076 - color: #ea9d34; 1077 - } 1078 - .wvc-variable { 1079 - color: #575279; 1080 - font-style: italic; 1081 - } 1082 - .wvc-keyword { 1083 - color: #286983; 1084 - } 1085 - .wvc-storage { 1086 - color: #56949f; 1087 - } 1088 - .wvc-storage.wvc-type { 1089 - color: #56949f; 1090 - } 1091 - .wvc-entity.wvc-name.wvc-class { 1092 - color: #286983; 1093 - font-weight: bold; 1094 - } 1095 - .wvc-entity.wvc-other.wvc-inherited-class { 1096 - color: #286983; 1097 - font-style: italic; 1098 - } 1099 - .wvc-entity.wvc-name.wvc-function { 1100 - color: #d7827e; 1101 - font-style: italic; 1102 - } 1103 - .wvc-variable.wvc-parameter { 1104 - color: #907aa9; 1105 - } 1106 - .wvc-entity.wvc-name.wvc-tag { 1107 - color: #286983; 1108 - font-weight: bold; 1109 - } 1110 - .wvc-entity.wvc-other.wvc-attribute-name { 1111 - color: #907aa9; 1112 - } 1113 - .wvc-support.wvc-function { 1114 - color: #d7827e; 1115 - font-weight: bold; 1116 - } 1117 - .wvc-support.wvc-constant { 1118 - color: #ea9d34; 1119 - font-weight: bold; 1120 - } 1121 - .wvc-support.wvc-type, .wvc-support.wvc-class { 1122 - color: #56949f; 1123 - font-weight: bold; 1124 - } 1125 - .wvc-support.wvc-other.wvc-variable { 1126 - color: #b4637a; 1127 - font-weight: bold; 1128 - } 1129 - .wvc-invalid { 1130 - color: #575279; 1131 - background-color: #b4637a; 1132 - } 1133 - .wvc-invalid.wvc-deprecated { 1134 - color: #575279; 1135 - background-color: #907aa9; 1136 - } 1137 - .wvc-punctuation, .wvc-keyword.wvc-operator { 1138 - color: #797593; 1139 - } 1140 - 1141 - 1142 - /* Syntax highlighting - Dark Mode */ 1143 - @media (prefers-color-scheme: dark) { 1144 - /* 1145 - * theme "Rosé Pine" generated by syntect 1146 - */ 1147 - 1148 - .wvc-code { 1149 - color: #e0def4; 1150 - background-color: #191724; 1151 - } 1152 - 1153 - .wvc-comment { 1154 - color: #908caa; 1155 - font-style: italic; 1156 - } 1157 - .wvc-string, .wvc-punctuation.wvc-definition.wvc-string { 1158 - color: #f6c177; 1159 - } 1160 - .wvc-constant.wvc-numeric { 1161 - color: #f6c177; 1162 - } 1163 - .wvc-constant.wvc-language { 1164 - color: #f6c177; 1165 - font-weight: bold; 1166 - } 1167 - .wvc-constant.wvc-character, .wvc-constant.wvc-other { 1168 - color: #f6c177; 1169 - } 1170 - .wvc-variable { 1171 - color: #e0def4; 1172 - font-style: italic; 1173 - } 1174 - .wvc-keyword { 1175 - color: #31748f; 1176 - } 1177 - .wvc-storage { 1178 - color: #9ccfd8; 1179 - } 1180 - .wvc-storage.wvc-type { 1181 - color: #9ccfd8; 1182 - } 1183 - .wvc-entity.wvc-name.wvc-class { 1184 - color: #31748f; 1185 - font-weight: bold; 1186 - } 1187 - .wvc-entity.wvc-other.wvc-inherited-class { 1188 - color: #31748f; 1189 - font-style: italic; 1190 - } 1191 - .wvc-entity.wvc-name.wvc-function { 1192 - color: #ebbcba; 1193 - font-style: italic; 1194 - } 1195 - .wvc-variable.wvc-parameter { 1196 - color: #c4a7e7; 1197 - } 1198 - .wvc-entity.wvc-name.wvc-tag { 1199 - color: #31748f; 1200 - font-weight: bold; 1201 - } 1202 - .wvc-entity.wvc-other.wvc-attribute-name { 1203 - color: #c4a7e7; 1204 - } 1205 - .wvc-support.wvc-function { 1206 - color: #ebbcba; 1207 - font-weight: bold; 1208 - } 1209 - .wvc-support.wvc-constant { 1210 - color: #f6c177; 1211 - font-weight: bold; 1212 - } 1213 - .wvc-support.wvc-type, .wvc-support.wvc-class { 1214 - color: #9ccfd8; 1215 - font-weight: bold; 1216 - } 1217 - .wvc-support.wvc-other.wvc-variable { 1218 - color: #eb6f92; 1219 - font-weight: bold; 1220 - } 1221 - .wvc-invalid { 1222 - color: #e0def4; 1223 - background-color: #eb6f92; 1224 - } 1225 - .wvc-invalid.wvc-deprecated { 1226 - color: #e0def4; 1227 - background-color: #c4a7e7; 1228 - } 1229 - .wvc-punctuation, .wvc-keyword.wvc-operator { 1230 - color: #908caa; 1231 - } 1232 - } 1233 - </style> 1234 - </head> 1235 - <body style="background: var(--color-base); min-height: 100vh;"> 1236 - <div class="notebook-content"> 1237 - <h1>Weaver: Long-form Writing on AT Protocol</h1> 1238 - <p><em>Or: "Get in kid, we're rebuilding the blogosphere!"</em></p> 1239 - <p>I grew up, like a lot of people on Bluesky, in the era of the internet where most of your online social interactions took place via text. I had a MySpace account, MSN messenger and Google Chat, I first got on Facebook back when they required a school email to sign up, I had a Tumblr, though not a LiveJournal.<label for="sn-1" class="sidenote-number"></label><input type="checkbox" id="sn-1" class="margin-toggle"/><span class="sidenote">Hi Rahaeli. Sorry I was the wrong kind of nerd.</span></p> 1240 - <blockquote> 1241 - <p><img src="weaver_photo_med.jpg" alt="weaver_photo_med.jpg" /><em>The namesake of what I'm building</em></p> 1242 - </blockquote> 1243 - <p>Social media in the conventional sense has been in a lot of ways a small part of the story of my time on the internet. The broader independent blogosphere of my teens and early adulthood shaped my worldview, and I was an avid reader and sometime participant there.</p> 1244 - <h2>The Blogosphere</h2> 1245 - <p>I am an atheist in large part because of a blog called Common Sense Atheism.<label for="sn-2" class="sidenote-number"></label><input type="checkbox" id="sn-2" class="margin-toggle"/><span class="sidenote">The author, Luke Muehlhauser, was criticising both Richard Dawkins <em>and</em> some Christian apologetics I was familiar with.</span> Luke's blog was part of a cluster of blogs out of which grew the rationalists, one of, for better or for worse, the most influential intellectual movements of the 21st century.I also read blogs like boingboing.net, was a big fan of Cory Doctorow. I figured out I am trans in part because of Thing of Things,<label for="sn-3" class="sidenote-number"></label><input type="checkbox" id="sn-3" class="margin-toggle"/><span class="sidenote">Specifically their piece on the <a href="https://thingofthings.wordpress.com/2017/05/05/the-cluster-structure-of-genderspace/">cluster structure of genderspace</a>.</span> a blog by Ozy Frantz, a transmasc person in the broader rationalist and Effective Altruist blogosphere.One thing these all have in common is length. Part of the reason I only really got onto Twitter in 2020 or so was because the concept of microblogging, of having to fit your thoughts into such a small package, baffled me for ages.<label for="sn-4" class="sidenote-number"></label><input type="checkbox" id="sn-4" class="margin-toggle"/><span class="sidenote">Amusingly I now think that being on Twitter and now Bluesky made me a better writer. Restrictions breed creativity, after all.</span></p> 1246 - <aside> 1247 - <blockquote> 1248 - <p><strong>On Platform Decay</strong></p> 1249 - <p>Through all of this I was never really satisfied with the options that were out there for long-form writing. Wordpress required too much setup. Tumblr's system for comments remains insane. Hosting my own seemed like too much money to burn on something nobody might read.</p> 1250 - </blockquote> 1251 - </aside> 1252 - <p>But at the same time, Substack's success proves that there is very much a desire for long-form writing, enough that people will pay for it, and that investors will back it. There are thoughts and forms of writing that you simply cannot fit into a post or even a thread of posts.</p> 1253 - <p>Plus, I'm loathe to enable a centralised platform like Substack where the owners are unfortunately friendly to fascists.<label for="sn-5" class="sidenote-number"></label><input type="checkbox" id="sn-5" class="margin-toggle"/><span class="sidenote">I am very much a fan of freedom of expression. I'm not so much a fan of paying money to Nazis.</span>That's where the <code>at://</code> protocol and Weaver comes in.</p> 1254 - <h2>The Pitch</h2> 1255 - <p>Weaver is designed to be a highly flexible platform for medium and long-form writing on atproto.<label for="sn-6" class="sidenote-number"></label><input type="checkbox" id="sn-6" class="margin-toggle"/><span class="sidenote">The weaver bird builds intricate, self-contained homes—seemed fitting for a platform about owning your writing.</span> I was inspired by how weaver birds build their own homes, and by the notebooks, physical and virtual, that I create in the course of my work.The initial proof-of-concept is essentially a static site generator, able to turn a Markdown text file or a folder of Markdown files into a static "notebook" site. The intermediate goal is an elegant and intuitive writing platform with collaborative editing and straightforward, immediate publishing via a web-app.</p> 1256 - <aside> 1257 - <blockquote> 1258 - <p><strong>The Ultimate Goal</strong></p> 1259 - <p>Build a platform suitable for professional writers and journalists, an open alternative to platforms like Substack, with ways for readers to support writers, all on the <code>at://</code> protocol.</p> 1260 - </blockquote> 1261 - </aside> 1262 - <h2>How It Works</h2> 1263 - <p>Weaver works on a concept of notebooks with entries, which can be grouped into pages or chapters. They can have multiple attributed authors. You can tear out a metaphorical page and stick it in another notebook.</p> 1264 - <p>You own what you write.<label for="sn-7" class="sidenote-number"></label><input type="checkbox" id="sn-7" class="margin-toggle"/><span class="sidenote">Technically you can include entries you don't control in your notebooks, although this isn't a supported mode—it's about <em>your</em> ownership of <em>your</em> words.</span> And once collaborative editing is in, collaborative work will be resilient against deletion by one author. They can delete their notebook or even their account, but what you write will be safe.Entries are Markdown text—specifically, an extension on the Obsidian flavour of Markdown.<label for="sn-8" class="sidenote-number"></label><input type="checkbox" id="sn-8" class="margin-toggle"/><span class="sidenote">I forked the popular rust markdown processing library <code>pulldown-cmark</code> because it had limited extensibility along the axes I wanted—custom syntax extensions to support Obsidian's Markdown flavour and additional useful features, like some of the ones on show here!</span> They support additional embed types, including atproto record embeds and other markdown documents, as well as resizable images.</p> 1265 - <h2>Why Rust?</h2> 1266 - <p>As to why I'm writing it in Rust (and currently zero Typescript) as opposed to Go and Typescript? Well it comes down to familiarity. Rust isn't necessarily anyone's first choice in a vacuum for a web-native programming language, but it works quite well as one. I can share the vast majority of the protocol code, as well as the markdown rendering engine, between front and back end, with few if any compromises on performance, save a larger bundle size due to the nature of WebAssembly.</p> 1267 - <aside> 1268 - <blockquote> 1269 - <p><strong>On Interoperability</strong></p> 1270 - <p>The <code>at://</code> protocol, while it was developed in concert with a microblogging app, is actually pretty damn good for "macroblogging" too. Weaver's app server can display Whitewind posts. With effort, it can faithfully render Leaflet posts. It doesn't care what app your profile is on.</p> 1271 - </blockquote> 1272 - </aside> 1273 - <h2>Evolution</h2> 1274 - <p>Weaver is therefore very much an evolving thing. It will always have and support the proof-of-concept workflow as a first-class citizen. That's part of the benefit of building this on atproto.</p> 1275 - <p>If I screw this up, not too hard for someone else to pick up the torch and continue.<label for="sn-9" class="sidenote-number"></label><input type="checkbox" id="sn-9" class="margin-toggle"/><span class="sidenote">This is the traditional footnote, at the end, because sometimes you want your citations at the bottom of the page rather than in the margins.</span></p> 1276 - </div> 1277 - </body> 1278 - </html>
···
test-output/weaver_photo_med.jpg

This is a binary file and will not be displayed.

test-sidenotes.md weaver_notes/test-sidenotes.md
+2 -2
weaver_notes/.obsidian/workspace.json
··· 199 "bases:Create new base": false 200 } 201 }, 202 - "active": "2f6bdb6d2dce13ed", 203 "lastOpenFiles": [ 204 "cargo_bloated.png", 205 "Writing the AppView Last.md", 206 "Untitled.md", ··· 215 "meta.png", 216 "Pasted image 20251114125028.png", 217 "invalid_record.png", 218 - "Pasted image 20251114125031.png", 219 "Arch.md", 220 "Weaver - Long-form writing.md" 221 ]
··· 199 "bases:Create new base": false 200 } 201 }, 202 + "active": "6029beecc3d03bce", 203 "lastOpenFiles": [ 204 + "test-sidenotes.md", 205 "cargo_bloated.png", 206 "Writing the AppView Last.md", 207 "Untitled.md", ··· 216 "meta.png", 217 "Pasted image 20251114125028.png", 218 "invalid_record.png", 219 "Arch.md", 220 "Weaver - Long-form writing.md" 221 ]