A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing

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 24 }, 25 25 "packages/cli": { 26 26 "name": "sequoia-cli", 27 - "version": "0.3.2", 27 + "version": "0.4.0", 28 28 "bin": { 29 29 "sequoia": "dist/index.js", 30 30 }, ··· 41 41 "devDependencies": { 42 42 "@biomejs/biome": "^2.3.13", 43 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 44 "@types/node": "^20", 56 45 }, 57 46 "peerDependencies": { ··· 1369 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=="], 1370 1359 1371 1360 "sequoia-cli": ["sequoia-cli@workspace:packages/cli"], 1372 - 1373 - "sequoia-ui": ["sequoia-ui@workspace:packages/ui"], 1374 1361 1375 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=="], 1376 1363
+1
docs/docs/pages/comments.mdx
··· 143 143 |-----------|------|---------|-------------| 144 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 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 | 146 147 147 148 ```html 148 149 <!-- Use attributes for explicit control -->
+56 -55
docs/docs/public/sequoia-comments.js
··· 14 14 * Attributes: 15 15 * - document-uri: AT Protocol URI for the document (optional if link tag exists) 16 16 * - depth: Maximum depth of nested replies to fetch (default: 6) 17 + * - hide: Set to "auto" to hide if no document link is detected 17 18 * 18 19 * CSS Custom Properties: 19 20 * - --sequoia-fg-color: Text color (default: #1f2937) ··· 573 574 class SequoiaComments extends BaseElement { 574 575 constructor() { 575 576 super(); 576 - this.shadow = this.attachShadow({ mode: "open" }); 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; 577 589 this.state = { type: "loading" }; 578 590 this.abortController = null; 591 + 579 592 } 580 593 581 594 static get observedAttributes() { 582 - return ["document-uri", "depth"]; 595 + return ["document-uri", "depth", "hide"]; 583 596 } 584 597 585 598 connectedCallback() { ··· 614 627 get depth() { 615 628 const depthAttr = this.getAttribute("depth"); 616 629 return depthAttr ? parseInt(depthAttr, 10) : 6; 630 + } 631 + 632 + get hide() { 633 + const hideAttr = this.getAttribute("hide"); 634 + return hideAttr === "auto"; 617 635 } 618 636 619 637 async loadComments() { ··· 666 684 } 667 685 668 686 render() { 669 - const styleTag = `<style>${styles}</style>`; 670 - 671 687 switch (this.state.type) { 672 688 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> 689 + this.commentsContainer.innerHTML = ` 690 + <div class="sequoia-loading"> 691 + <span class="sequoia-loading-spinner"></span> 692 + Loading comments... 680 693 </div> 681 694 `; 682 695 break; 683 696 684 697 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> 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. 691 701 </div> 692 702 `; 703 + if (this.hide) { 704 + this.commentsContainer.style.display = 'none'; 705 + } 693 706 break; 694 707 695 708 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> 709 + this.commentsContainer.innerHTML = ` 710 + <div class="sequoia-empty"> 711 + Comments are not enabled for this post. 702 712 </div> 703 713 `; 704 714 break; 705 715 706 716 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> 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! 720 727 </div> 721 728 `; 722 729 break; 723 730 724 731 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> 732 + this.commentsContainer.innerHTML = ` 733 + <div class="sequoia-error"> 734 + Failed to load comments: ${escapeHtml(this.state.message)} 731 735 </div> 732 736 `; 733 737 break; ··· 740 744 .join(""); 741 745 const commentCount = this.countComments(replies); 742 746 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> 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} 756 757 </div> 757 758 `; 758 759 break;
+56 -55
packages/cli/src/components/sequoia-comments.js
··· 14 14 * Attributes: 15 15 * - document-uri: AT Protocol URI for the document (optional if link tag exists) 16 16 * - depth: Maximum depth of nested replies to fetch (default: 6) 17 + * - hide: Set to "auto" to hide if no document link is detected 17 18 * 18 19 * CSS Custom Properties: 19 20 * - --sequoia-fg-color: Text color (default: #1f2937) ··· 573 574 class SequoiaComments extends BaseElement { 574 575 constructor() { 575 576 super(); 576 - this.shadow = this.attachShadow({ mode: "open" }); 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; 577 589 this.state = { type: "loading" }; 578 590 this.abortController = null; 591 + 579 592 } 580 593 581 594 static get observedAttributes() { 582 - return ["document-uri", "depth"]; 595 + return ["document-uri", "depth", "hide"]; 583 596 } 584 597 585 598 connectedCallback() { ··· 614 627 get depth() { 615 628 const depthAttr = this.getAttribute("depth"); 616 629 return depthAttr ? parseInt(depthAttr, 10) : 6; 630 + } 631 + 632 + get hide() { 633 + const hideAttr = this.getAttribute("hide"); 634 + return hideAttr === "auto"; 617 635 } 618 636 619 637 async loadComments() { ··· 666 684 } 667 685 668 686 render() { 669 - const styleTag = `<style>${styles}</style>`; 670 - 671 687 switch (this.state.type) { 672 688 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> 689 + this.commentsContainer.innerHTML = ` 690 + <div class="sequoia-loading"> 691 + <span class="sequoia-loading-spinner"></span> 692 + Loading comments... 680 693 </div> 681 694 `; 682 695 break; 683 696 684 697 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> 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. 691 701 </div> 692 702 `; 703 + if (this.hide) { 704 + this.commentsContainer.style.display = 'none'; 705 + } 693 706 break; 694 707 695 708 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> 709 + this.commentsContainer.innerHTML = ` 710 + <div class="sequoia-empty"> 711 + Comments are not enabled for this post. 702 712 </div> 703 713 `; 704 714 break; 705 715 706 716 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> 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! 720 727 </div> 721 728 `; 722 729 break; 723 730 724 731 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> 732 + this.commentsContainer.innerHTML = ` 733 + <div class="sequoia-error"> 734 + Failed to load comments: ${escapeHtml(this.state.message)} 731 735 </div> 732 736 `; 733 737 break; ··· 740 744 .join(""); 741 745 const commentCount = this.countComments(replies); 742 746 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> 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} 756 757 </div> 757 758 `; 758 759 break;