A CLI for publishing standard.site documents to ATProto

Add hide attribute to sequoia-comments component

Resolves #1

authored by

Heath Stewart and committed by tangled.org 9ef18c3f 08e41d32

+114 -124
+1 -14
bun.lock
··· 24 }, 25 "packages/cli": { 26 "name": "sequoia-cli", 27 - "version": "0.3.2", 28 "bin": { 29 "sequoia": "dist/index.js", 30 }, ··· 41 "devDependencies": { 42 "@biomejs/biome": "^2.3.13", 43 "@types/mime-types": "^3.0.1", 44 - "@types/node": "^20", 45 - }, 46 - "peerDependencies": { 47 - "typescript": "^5", 48 - }, 49 - }, 50 - "packages/ui": { 51 - "name": "sequoia-ui", 52 - "version": "0.1.0", 53 - "devDependencies": { 54 - "@biomejs/biome": "^2.3.13", 55 "@types/node": "^20", 56 }, 57 "peerDependencies": { ··· 1369 "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], 1370 1371 "sequoia-cli": ["sequoia-cli@workspace:packages/cli"], 1372 - 1373 - "sequoia-ui": ["sequoia-ui@workspace:packages/ui"], 1374 1375 "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], 1376
··· 24 }, 25 "packages/cli": { 26 "name": "sequoia-cli", 27 + "version": "0.4.0", 28 "bin": { 29 "sequoia": "dist/index.js", 30 }, ··· 41 "devDependencies": { 42 "@biomejs/biome": "^2.3.13", 43 "@types/mime-types": "^3.0.1", 44 "@types/node": "^20", 45 }, 46 "peerDependencies": { ··· 1358 "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], 1359 1360 "sequoia-cli": ["sequoia-cli@workspace:packages/cli"], 1361 1362 "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], 1363
+1
docs/docs/pages/comments.mdx
··· 143 |-----------|------|---------|-------------| 144 | `document-uri` | `string` | - | AT Protocol URI for the document. Optional if a `<link rel="site.standard.document">` tag exists in the page head. | 145 | `depth` | `number` | `6` | Maximum depth of nested replies to fetch. | 146 147 ```html 148 <!-- Use attributes for explicit control -->
··· 143 |-----------|------|---------|-------------| 144 | `document-uri` | `string` | - | AT Protocol URI for the document. Optional if a `<link rel="site.standard.document">` tag exists in the page head. | 145 | `depth` | `number` | `6` | Maximum depth of nested replies to fetch. | 146 + | `hide` | `string` | - | Set to "auto" to hide if no document link is detected | 147 148 ```html 149 <!-- Use attributes for explicit control -->
+56 -55
docs/docs/public/sequoia-comments.js
··· 14 * Attributes: 15 * - document-uri: AT Protocol URI for the document (optional if link tag exists) 16 * - depth: Maximum depth of nested replies to fetch (default: 6) 17 * 18 * CSS Custom Properties: 19 * - --sequoia-fg-color: Text color (default: #1f2937) ··· 573 class SequoiaComments extends BaseElement { 574 constructor() { 575 super(); 576 - this.shadow = this.attachShadow({ mode: "open" }); 577 this.state = { type: "loading" }; 578 this.abortController = null; 579 } 580 581 static get observedAttributes() { 582 - return ["document-uri", "depth"]; 583 } 584 585 connectedCallback() { ··· 614 get depth() { 615 const depthAttr = this.getAttribute("depth"); 616 return depthAttr ? parseInt(depthAttr, 10) : 6; 617 } 618 619 async loadComments() { ··· 666 } 667 668 render() { 669 - const styleTag = `<style>${styles}</style>`; 670 - 671 switch (this.state.type) { 672 case "loading": 673 - this.shadow.innerHTML = ` 674 - ${styleTag} 675 - <div class="sequoia-comments-container"> 676 - <div class="sequoia-loading"> 677 - <span class="sequoia-loading-spinner"></span> 678 - Loading comments... 679 - </div> 680 </div> 681 `; 682 break; 683 684 case "no-document": 685 - this.shadow.innerHTML = ` 686 - ${styleTag} 687 - <div class="sequoia-comments-container"> 688 - <div class="sequoia-warning"> 689 - No document found. Add a <code>&lt;link rel="site.standard.document" href="at://..."&gt;</code> tag to your page. 690 - </div> 691 </div> 692 `; 693 break; 694 695 case "no-comments-enabled": 696 - this.shadow.innerHTML = ` 697 - ${styleTag} 698 - <div class="sequoia-comments-container"> 699 - <div class="sequoia-empty"> 700 - Comments are not enabled for this post. 701 - </div> 702 </div> 703 `; 704 break; 705 706 case "empty": 707 - this.shadow.innerHTML = ` 708 - ${styleTag} 709 - <div class="sequoia-comments-container"> 710 - <div class="sequoia-comments-header"> 711 - <h3 class="sequoia-comments-title">Comments</h3> 712 - <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 713 - ${BLUESKY_ICON} 714 - Reply on Bluesky 715 - </a> 716 - </div> 717 - <div class="sequoia-empty"> 718 - No comments yet. Be the first to reply on Bluesky! 719 - </div> 720 </div> 721 `; 722 break; 723 724 case "error": 725 - this.shadow.innerHTML = ` 726 - ${styleTag} 727 - <div class="sequoia-comments-container"> 728 - <div class="sequoia-error"> 729 - Failed to load comments: ${escapeHtml(this.state.message)} 730 - </div> 731 </div> 732 `; 733 break; ··· 740 .join(""); 741 const commentCount = this.countComments(replies); 742 743 - this.shadow.innerHTML = ` 744 - ${styleTag} 745 - <div class="sequoia-comments-container"> 746 - <div class="sequoia-comments-header"> 747 - <h3 class="sequoia-comments-title">${commentCount} Comment${commentCount !== 1 ? "s" : ""}</h3> 748 - <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 749 - ${BLUESKY_ICON} 750 - Reply on Bluesky 751 - </a> 752 - </div> 753 - <div class="sequoia-comments-list"> 754 - ${threadsHtml} 755 - </div> 756 </div> 757 `; 758 break;
··· 14 * Attributes: 15 * - document-uri: AT Protocol URI for the document (optional if link tag exists) 16 * - depth: Maximum depth of nested replies to fetch (default: 6) 17 + * - hide: Set to "auto" to hide if no document link is detected 18 * 19 * CSS Custom Properties: 20 * - --sequoia-fg-color: Text color (default: #1f2937) ··· 574 class SequoiaComments extends BaseElement { 575 constructor() { 576 super(); 577 + const shadow = this.attachShadow({ mode: "open" }); 578 + 579 + const styleTag = document.createElement("style"); 580 + shadow.appendChild(styleTag); 581 + styleTag.innerText = styles; 582 + 583 + const container = document.createElement("div"); 584 + shadow.appendChild(container); 585 + container.className = "sequoia-comments-container"; 586 + container.part = "container"; 587 + 588 + this.commentsContainer = container; 589 this.state = { type: "loading" }; 590 this.abortController = null; 591 + 592 } 593 594 static get observedAttributes() { 595 + return ["document-uri", "depth", "hide"]; 596 } 597 598 connectedCallback() { ··· 627 get depth() { 628 const depthAttr = this.getAttribute("depth"); 629 return depthAttr ? parseInt(depthAttr, 10) : 6; 630 + } 631 + 632 + get hide() { 633 + const hideAttr = this.getAttribute("hide"); 634 + return hideAttr === "auto"; 635 } 636 637 async loadComments() { ··· 684 } 685 686 render() { 687 switch (this.state.type) { 688 case "loading": 689 + this.commentsContainer.innerHTML = ` 690 + <div class="sequoia-loading"> 691 + <span class="sequoia-loading-spinner"></span> 692 + Loading comments... 693 </div> 694 `; 695 break; 696 697 case "no-document": 698 + this.commentsContainer.innerHTML = ` 699 + <div class="sequoia-warning"> 700 + No document found. Add a <code>&lt;link rel="site.standard.document" href="at://..."&gt;</code> tag to your page. 701 </div> 702 `; 703 + if (this.hide) { 704 + this.commentsContainer.style.display = 'none'; 705 + } 706 break; 707 708 case "no-comments-enabled": 709 + this.commentsContainer.innerHTML = ` 710 + <div class="sequoia-empty"> 711 + Comments are not enabled for this post. 712 </div> 713 `; 714 break; 715 716 case "empty": 717 + this.commentsContainer.innerHTML = ` 718 + <div class="sequoia-comments-header"> 719 + <h3 class="sequoia-comments-title">Comments</h3> 720 + <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 721 + ${BLUESKY_ICON} 722 + Reply on Bluesky 723 + </a> 724 + </div> 725 + <div class="sequoia-empty"> 726 + No comments yet. Be the first to reply on Bluesky! 727 </div> 728 `; 729 break; 730 731 case "error": 732 + this.commentsContainer.innerHTML = ` 733 + <div class="sequoia-error"> 734 + Failed to load comments: ${escapeHtml(this.state.message)} 735 </div> 736 `; 737 break; ··· 744 .join(""); 745 const commentCount = this.countComments(replies); 746 747 + this.commentsContainer.innerHTML = ` 748 + <div class="sequoia-comments-header"> 749 + <h3 class="sequoia-comments-title">${commentCount} Comment${commentCount !== 1 ? "s" : ""}</h3> 750 + <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 751 + ${BLUESKY_ICON} 752 + Reply on Bluesky 753 + </a> 754 + </div> 755 + <div class="sequoia-comments-list"> 756 + ${threadsHtml} 757 </div> 758 `; 759 break;
+56 -55
packages/cli/src/components/sequoia-comments.js
··· 14 * Attributes: 15 * - document-uri: AT Protocol URI for the document (optional if link tag exists) 16 * - depth: Maximum depth of nested replies to fetch (default: 6) 17 * 18 * CSS Custom Properties: 19 * - --sequoia-fg-color: Text color (default: #1f2937) ··· 573 class SequoiaComments extends BaseElement { 574 constructor() { 575 super(); 576 - this.shadow = this.attachShadow({ mode: "open" }); 577 this.state = { type: "loading" }; 578 this.abortController = null; 579 } 580 581 static get observedAttributes() { 582 - return ["document-uri", "depth"]; 583 } 584 585 connectedCallback() { ··· 614 get depth() { 615 const depthAttr = this.getAttribute("depth"); 616 return depthAttr ? parseInt(depthAttr, 10) : 6; 617 } 618 619 async loadComments() { ··· 666 } 667 668 render() { 669 - const styleTag = `<style>${styles}</style>`; 670 - 671 switch (this.state.type) { 672 case "loading": 673 - this.shadow.innerHTML = ` 674 - ${styleTag} 675 - <div class="sequoia-comments-container"> 676 - <div class="sequoia-loading"> 677 - <span class="sequoia-loading-spinner"></span> 678 - Loading comments... 679 - </div> 680 </div> 681 `; 682 break; 683 684 case "no-document": 685 - this.shadow.innerHTML = ` 686 - ${styleTag} 687 - <div class="sequoia-comments-container"> 688 - <div class="sequoia-warning"> 689 - No document found. Add a <code>&lt;link rel="site.standard.document" href="at://..."&gt;</code> tag to your page. 690 - </div> 691 </div> 692 `; 693 break; 694 695 case "no-comments-enabled": 696 - this.shadow.innerHTML = ` 697 - ${styleTag} 698 - <div class="sequoia-comments-container"> 699 - <div class="sequoia-empty"> 700 - Comments are not enabled for this post. 701 - </div> 702 </div> 703 `; 704 break; 705 706 case "empty": 707 - this.shadow.innerHTML = ` 708 - ${styleTag} 709 - <div class="sequoia-comments-container"> 710 - <div class="sequoia-comments-header"> 711 - <h3 class="sequoia-comments-title">Comments</h3> 712 - <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 713 - ${BLUESKY_ICON} 714 - Reply on Bluesky 715 - </a> 716 - </div> 717 - <div class="sequoia-empty"> 718 - No comments yet. Be the first to reply on Bluesky! 719 - </div> 720 </div> 721 `; 722 break; 723 724 case "error": 725 - this.shadow.innerHTML = ` 726 - ${styleTag} 727 - <div class="sequoia-comments-container"> 728 - <div class="sequoia-error"> 729 - Failed to load comments: ${escapeHtml(this.state.message)} 730 - </div> 731 </div> 732 `; 733 break; ··· 740 .join(""); 741 const commentCount = this.countComments(replies); 742 743 - this.shadow.innerHTML = ` 744 - ${styleTag} 745 - <div class="sequoia-comments-container"> 746 - <div class="sequoia-comments-header"> 747 - <h3 class="sequoia-comments-title">${commentCount} Comment${commentCount !== 1 ? "s" : ""}</h3> 748 - <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 749 - ${BLUESKY_ICON} 750 - Reply on Bluesky 751 - </a> 752 - </div> 753 - <div class="sequoia-comments-list"> 754 - ${threadsHtml} 755 - </div> 756 </div> 757 `; 758 break;
··· 14 * Attributes: 15 * - document-uri: AT Protocol URI for the document (optional if link tag exists) 16 * - depth: Maximum depth of nested replies to fetch (default: 6) 17 + * - hide: Set to "auto" to hide if no document link is detected 18 * 19 * CSS Custom Properties: 20 * - --sequoia-fg-color: Text color (default: #1f2937) ··· 574 class SequoiaComments extends BaseElement { 575 constructor() { 576 super(); 577 + const shadow = this.attachShadow({ mode: "open" }); 578 + 579 + const styleTag = document.createElement("style"); 580 + shadow.appendChild(styleTag); 581 + styleTag.innerText = styles; 582 + 583 + const container = document.createElement("div"); 584 + shadow.appendChild(container); 585 + container.className = "sequoia-comments-container"; 586 + container.part = "container"; 587 + 588 + this.commentsContainer = container; 589 this.state = { type: "loading" }; 590 this.abortController = null; 591 + 592 } 593 594 static get observedAttributes() { 595 + return ["document-uri", "depth", "hide"]; 596 } 597 598 connectedCallback() { ··· 627 get depth() { 628 const depthAttr = this.getAttribute("depth"); 629 return depthAttr ? parseInt(depthAttr, 10) : 6; 630 + } 631 + 632 + get hide() { 633 + const hideAttr = this.getAttribute("hide"); 634 + return hideAttr === "auto"; 635 } 636 637 async loadComments() { ··· 684 } 685 686 render() { 687 switch (this.state.type) { 688 case "loading": 689 + this.commentsContainer.innerHTML = ` 690 + <div class="sequoia-loading"> 691 + <span class="sequoia-loading-spinner"></span> 692 + Loading comments... 693 </div> 694 `; 695 break; 696 697 case "no-document": 698 + this.commentsContainer.innerHTML = ` 699 + <div class="sequoia-warning"> 700 + No document found. Add a <code>&lt;link rel="site.standard.document" href="at://..."&gt;</code> tag to your page. 701 </div> 702 `; 703 + if (this.hide) { 704 + this.commentsContainer.style.display = 'none'; 705 + } 706 break; 707 708 case "no-comments-enabled": 709 + this.commentsContainer.innerHTML = ` 710 + <div class="sequoia-empty"> 711 + Comments are not enabled for this post. 712 </div> 713 `; 714 break; 715 716 case "empty": 717 + this.commentsContainer.innerHTML = ` 718 + <div class="sequoia-comments-header"> 719 + <h3 class="sequoia-comments-title">Comments</h3> 720 + <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 721 + ${BLUESKY_ICON} 722 + Reply on Bluesky 723 + </a> 724 + </div> 725 + <div class="sequoia-empty"> 726 + No comments yet. Be the first to reply on Bluesky! 727 </div> 728 `; 729 break; 730 731 case "error": 732 + this.commentsContainer.innerHTML = ` 733 + <div class="sequoia-error"> 734 + Failed to load comments: ${escapeHtml(this.state.message)} 735 </div> 736 `; 737 break; ··· 744 .join(""); 745 const commentCount = this.countComments(replies); 746 747 + this.commentsContainer.innerHTML = ` 748 + <div class="sequoia-comments-header"> 749 + <h3 class="sequoia-comments-title">${commentCount} Comment${commentCount !== 1 ? "s" : ""}</h3> 750 + <a href="${this.state.postUrl}" target="_blank" rel="noopener noreferrer" class="sequoia-reply-button"> 751 + ${BLUESKY_ICON} 752 + Reply on Bluesky 753 + </a> 754 + </div> 755 + <div class="sequoia-comments-list"> 756 + ${threadsHtml} 757 </div> 758 `; 759 break;