diff --git a/packages/cli/src/commands/sync.ts b/packages/cli/src/commands/sync.ts index 55489bd..226b891 100644 --- a/packages/cli/src/commands/sync.ts +++ b/packages/cli/src/commands/sync.ts @@ -13,6 +13,7 @@ import { createAgent, listDocuments } from "../lib/atproto"; import { scanContentDirectory, getContentHash, + getTextContent, updateFrontmatterWithAtUri, resolvePostPath, } from "../lib/markdown"; @@ -181,8 +182,22 @@ export const syncCommand = command({ log.message(` URI: ${doc.uri}`); log.message(` File: ${path.basename(localPost.filePath)}`); - // Update state (use relative path from config directory) - const contentHash = await getContentHash(localPost.rawContent); + // Compare local text content with PDS text content to detect changes. + // We must avoid storing the local rawContent hash blindly, because + // that would make publish think nothing changed even when content + // was modified since the last publish. + const localTextContent = getTextContent( + localPost, + config.textContentField, + ); + const contentMatchesPDS = + localTextContent.slice(0, 10000) === doc.value.textContent; + + // If local content matches PDS, store the local hash (up to date). + // If it differs, store empty hash so publish detects the change. + const contentHash = contentMatchesPDS + ? await getContentHash(localPost.rawContent) + : ""; const relativeFilePath = path.relative(configDir, localPost.filePath); state.posts[relativeFilePath] = { contentHash, diff --git a/packages/cli/src/lib/atproto.ts b/packages/cli/src/lib/atproto.ts index b999c98..31123f2 100644 --- a/packages/cli/src/lib/atproto.ts +++ b/packages/cli/src/lib/atproto.ts @@ -2,7 +2,7 @@ import { Agent, AtpAgent } from "@atproto/api"; import * as mimeTypes from "mime-types"; import * as fs from "node:fs/promises"; import * as path from "node:path"; -import { stripMarkdownForText, resolvePostPath } from "./markdown"; +import { getTextContent, resolvePostPath } from "./markdown"; import { getOAuthClient } from "./oauth-client"; import type { BlobObject, @@ -251,17 +251,7 @@ export async function createDocument( config.pathTemplate, ); const publishDate = new Date(post.frontmatter.publishDate); - - // Determine textContent: use configured field from frontmatter, or fallback to markdown body - let textContent: string; - if ( - config.textContentField && - post.rawFrontmatter?.[config.textContentField] - ) { - textContent = String(post.rawFrontmatter[config.textContentField]); - } else { - textContent = stripMarkdownForText(post.content); - } + const textContent = getTextContent(post, config.textContentField); const record: Record = { $type: "site.standard.document", @@ -316,17 +306,7 @@ export async function updateDocument( config.pathTemplate, ); const publishDate = new Date(post.frontmatter.publishDate); - - // Determine textContent: use configured field from frontmatter, or fallback to markdown body - let textContent: string; - if ( - config.textContentField && - post.rawFrontmatter?.[config.textContentField] - ) { - textContent = String(post.rawFrontmatter[config.textContentField]); - } else { - textContent = stripMarkdownForText(post.content); - } + const textContent = getTextContent(post, config.textContentField); const record: Record = { $type: "site.standard.document", diff --git a/packages/cli/src/lib/markdown.ts b/packages/cli/src/lib/markdown.ts index 2d1afbd..0f2f27d 100644 --- a/packages/cli/src/lib/markdown.ts +++ b/packages/cli/src/lib/markdown.ts @@ -435,3 +435,13 @@ export function stripMarkdownForText(markdown: string): string { .replace(/\n{3,}/g, "\n\n") // Normalize multiple newlines .trim(); } + +export function getTextContent( + post: { content: string; rawFrontmatter?: Record }, + textContentField?: string, +): string { + if (textContentField && post.rawFrontmatter?.[textContentField]) { + return String(post.rawFrontmatter[textContentField]); + } + return stripMarkdownForText(post.content); +}