sequoia-cli 0.4.0-alpha.0 → 0.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@
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,13 +574,25 @@ const BaseElement = typeof HTMLElement !== "undefined" ? HTMLElement : class {};
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() {
@@ -616,6 +629,11 @@ class SequoiaComments extends BaseElement {
616
629
  return depthAttr ? parseInt(depthAttr, 10) : 6;
617
630
  }
618
631
 
632
+ get hide() {
633
+ const hideAttr = this.getAttribute("hide");
634
+ return hideAttr === "auto";
635
+ }
636
+
619
637
  async loadComments() {
620
638
  // Cancel any in-flight request
621
639
  this.abortController?.abort();
@@ -666,68 +684,54 @@ class SequoiaComments extends BaseElement {
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,19 +744,16 @@ class SequoiaComments extends BaseElement {
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;
@@ -800,9 +801,9 @@ class SequoiaComments extends BaseElement {
800
801
  * Render a single comment
801
802
  * @param {any} post - Post data
802
803
  * @param {boolean} showThreadLine - Whether to show the connecting thread line
803
- * @param {number} index - Index in the flattened thread (0 = top-level)
804
+ * @param {number} _index - Index in the flattened thread (0 = top-level)
804
805
  */
805
- renderComment(post, showThreadLine = false, index = 0) {
806
+ renderComment(post, showThreadLine = false, _index = 0) {
806
807
  const author = post.author;
807
808
  const displayName = author.displayName || author.handle;
808
809
  const avatarHtml = author.avatar
package/dist/index.js CHANGED
@@ -93241,8 +93241,8 @@ ${l}
93241
93241
  } }).prompt();
93242
93242
 
93243
93243
  // src/commands/add.ts
93244
- import { fileURLToPath } from "url";
93245
- import { dirname as dirname2 } from "path";
93244
+ import { fileURLToPath } from "node:url";
93245
+ import { dirname as dirname2 } from "node:path";
93246
93246
 
93247
93247
  // src/lib/config.ts
93248
93248
  import * as fs from "node:fs/promises";
@@ -93326,6 +93326,9 @@ function generateConfigTemplate(options) {
93326
93326
  if (options.stripDatePrefix) {
93327
93327
  config.stripDatePrefix = options.stripDatePrefix;
93328
93328
  }
93329
+ if (options.pathTemplate) {
93330
+ config.pathTemplate = options.pathTemplate;
93331
+ }
93329
93332
  if (options.textContentField) {
93330
93333
  config.textContentField = options.textContentField;
93331
93334
  }
@@ -99201,6 +99204,32 @@ function getSlugFromOptions(relativePath, rawFrontmatter, options = {}) {
99201
99204
  }
99202
99205
  return slug;
99203
99206
  }
99207
+ function resolvePathTemplate(template, post) {
99208
+ const publishDate = new Date(post.frontmatter.publishDate);
99209
+ const year = String(publishDate.getFullYear());
99210
+ const month = String(publishDate.getMonth() + 1).padStart(2, "0");
99211
+ const day = String(publishDate.getDate()).padStart(2, "0");
99212
+ const slugifiedTitle = (post.frontmatter.title || "").toLowerCase().replace(/\s+/g, "-").replace(/[^\w-]/g, "");
99213
+ let result = template.replace(/\{slug\}/g, post.slug).replace(/\{year\}/g, year).replace(/\{month\}/g, month).replace(/\{day\}/g, day).replace(/\{title\}/g, slugifiedTitle);
99214
+ result = result.replace(/\{(\w+)\}/g, (_match, field) => {
99215
+ const value = post.rawFrontmatter[field];
99216
+ if (value != null && typeof value === "string") {
99217
+ return value;
99218
+ }
99219
+ return "";
99220
+ });
99221
+ if (!result.startsWith("/")) {
99222
+ result = `/${result}`;
99223
+ }
99224
+ return result;
99225
+ }
99226
+ function resolvePostPath(post, pathPrefix, pathTemplate) {
99227
+ if (pathTemplate) {
99228
+ return resolvePathTemplate(pathTemplate, post);
99229
+ }
99230
+ const prefix = pathPrefix || "/posts";
99231
+ return `${prefix}/${post.slug}`;
99232
+ }
99204
99233
  async function getContentHash(content) {
99205
99234
  const encoder = new TextEncoder;
99206
99235
  const data = encoder.encode(content);
@@ -99436,7 +99465,7 @@ async function getOAuthClient() {
99436
99465
  clientMetadata: {
99437
99466
  client_id: `http://localhost?${clientIdParams.toString()}`,
99438
99467
  client_name: "Sequoia CLI",
99439
- client_uri: "https://github.com/stevedylandev/sequoia",
99468
+ client_uri: "https://sequoia.pub",
99440
99469
  redirect_uris: [CALLBACK_URL],
99441
99470
  grant_types: ["authorization_code", "refresh_token"],
99442
99471
  response_types: ["code"],
@@ -99603,8 +99632,7 @@ async function resolveImagePath(ogImage, imagesDir, contentDir) {
99603
99632
  return null;
99604
99633
  }
99605
99634
  async function createDocument(agent, post, config, coverImage) {
99606
- const pathPrefix = config.pathPrefix || "/posts";
99607
- const postPath = `${pathPrefix}/${post.slug}`;
99635
+ const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate);
99608
99636
  const publishDate = new Date(post.frontmatter.publishDate);
99609
99637
  let textContent;
99610
99638
  if (config.textContentField && post.rawFrontmatter?.[config.textContentField]) {
@@ -99643,8 +99671,7 @@ async function updateDocument(agent, post, atUri, config, coverImage) {
99643
99671
  throw new Error(`Invalid atUri format: ${atUri}`);
99644
99672
  }
99645
99673
  const [, , collection, rkey] = uriMatch;
99646
- const pathPrefix = config.pathPrefix || "/posts";
99647
- const postPath = `${pathPrefix}/${post.slug}`;
99674
+ const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate);
99648
99675
  const publishDate = new Date(post.frontmatter.publishDate);
99649
99676
  let textContent;
99650
99677
  if (config.textContentField && post.rawFrontmatter?.[config.textContentField]) {
@@ -100923,9 +100950,14 @@ var publishCommand = import_cmd_ts6.command({
100923
100950
  long: "dry-run",
100924
100951
  short: "n",
100925
100952
  description: "Preview what would be published without making changes"
100953
+ }),
100954
+ verbose: import_cmd_ts6.flag({
100955
+ long: "verbose",
100956
+ short: "v",
100957
+ description: "Show more information"
100926
100958
  })
100927
100959
  },
100928
- handler: async ({ force, dryRun }) => {
100960
+ handler: async ({ force, dryRun, verbose }) => {
100929
100961
  const configPath = await findConfig();
100930
100962
  if (!configPath) {
100931
100963
  R2.error("No publisher.config.ts found. Run 'publisher init' first.");
@@ -101056,7 +101088,13 @@ ${postsToPublish.length} posts to publish:
101056
101088
  }
101057
101089
  }
101058
101090
  }
101059
- R2.message(` ${icon} ${post.frontmatter.title} (${reason})${bskyNote}`);
101091
+ let postUrl = "";
101092
+ if (verbose) {
101093
+ const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate);
101094
+ postUrl = `
101095
+ ${config.siteUrl}${postPath}`;
101096
+ }
101097
+ R2.message(` ${icon} ${post.frontmatter.title} (${reason})${bskyNote}${postUrl}`);
101060
101098
  }
101061
101099
  if (dryRun) {
101062
101100
  if (blueskyEnabled) {
@@ -101128,8 +101166,7 @@ Dry run complete. No changes made.`);
101128
101166
  R2.info(` Post is older than ${maxAgeDays} days, skipping Bluesky post`);
101129
101167
  } else {
101130
101168
  try {
101131
- const pathPrefix = config.pathPrefix || "/posts";
101132
- const canonicalUrl = `${config.siteUrl}${pathPrefix}/${post.slug}`;
101169
+ const canonicalUrl = `${config.siteUrl}${resolvePostPath(post, config.pathPrefix, config.pathTemplate)}`;
101133
101170
  bskyPostRef = await createBlueskyPost(agent, {
101134
101171
  title: post.frontmatter.title,
101135
101172
  description: post.frontmatter.description,
@@ -101278,10 +101315,9 @@ var syncCommand = import_cmd_ts7.command({
101278
101315
  stripDatePrefix: config.stripDatePrefix
101279
101316
  });
101280
101317
  s.stop(`Found ${localPosts.length} local posts`);
101281
- const pathPrefix = config.pathPrefix || "/posts";
101282
101318
  const postsByPath = new Map;
101283
101319
  for (const post of localPosts) {
101284
- const postPath = `${pathPrefix}/${post.slug}`;
101320
+ const postPath = resolvePostPath(post, config.pathPrefix, config.pathTemplate);
101285
101321
  postsByPath.set(postPath, post);
101286
101322
  }
101287
101323
  const state = await loadState(configDir);
@@ -101460,6 +101496,7 @@ async function updateConfigFlow(config, configPath) {
101460
101496
  ignore: configUpdated.ignore,
101461
101497
  removeIndexFromSlug: configUpdated.removeIndexFromSlug,
101462
101498
  stripDatePrefix: configUpdated.stripDatePrefix,
101499
+ pathTemplate: configUpdated.pathTemplate,
101463
101500
  textContentField: configUpdated.textContentField,
101464
101501
  bluesky: configUpdated.bluesky
101465
101502
  });
@@ -101805,7 +101842,7 @@ Publish evergreen content to the ATmosphere
101805
101842
 
101806
101843
  > https://tangled.org/stevedylan.dev/sequoia
101807
101844
  `,
101808
- version: "0.4.0",
101845
+ version: "0.5.0-alpha.0",
101809
101846
  cmds: {
101810
101847
  add: addCommand,
101811
101848
  auth: authCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sequoia-cli",
3
- "version": "0.4.0-alpha.0",
3
+ "version": "0.5.0-alpha.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sequoia": "dist/index.js"