@rmdes/indiekit-syndicator-bluesky 1.0.4 → 1.0.6

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.
package/lib/bluesky.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  getPostImage,
7
7
  getPostText,
8
8
  getLikePostText,
9
+ buildPostText,
9
10
  getPostParts,
10
11
  uriToPostUrl,
11
12
  fetchOpenGraphData,
@@ -365,16 +366,19 @@ export class Bluesky {
365
366
  return this.postPost(richText, { images, externalEmbed });
366
367
  }
367
368
 
368
- // Regular post - check for external URL in content to create link card
369
- const text = getPostText(properties, this.includePermalink);
369
+ // Regular post - determine external URL and build text accordingly
370
+ const externalUrl = getExternalUrl(properties);
371
+ const text = buildPostText(properties, { externalUrl });
370
372
  const richText = await createRichText(client, text);
371
373
 
372
- // If no images, try to create external embed from URLs in content
374
+ // Create OG embed:
375
+ // - External URL exists → use it as OG card (permalink is in text)
376
+ // - No external URL → use permalink as OG card
373
377
  let externalEmbed = null;
374
378
  if (!images?.length) {
375
- const externalUrl = getExternalUrl(properties);
376
- if (externalUrl) {
377
- externalEmbed = await this.createExternalEmbed(externalUrl);
379
+ const embedUrl = externalUrl || properties.url;
380
+ if (embedUrl) {
381
+ externalEmbed = await this.createExternalEmbed(embedUrl);
378
382
  }
379
383
  }
380
384
 
package/lib/utils.js CHANGED
@@ -180,7 +180,7 @@ export const uriToPostUrl = (profileUrl, uri) => {
180
180
  };
181
181
 
182
182
  /**
183
- * Get post text from given JF2 properties
183
+ * Get post text from given JF2 properties (legacy, used for likes/bookmarks)
184
184
  * @param {object} properties - JF2 properties
185
185
  * @param {boolean} [includePermalink] - Include permalink in post
186
186
  * @returns {string} Post text
@@ -197,8 +197,9 @@ export const getPostText = (properties, includePermalink) => {
197
197
  }
198
198
 
199
199
  // Truncate status if longer than 300 characters
200
+ // ALWAYS include permalink when truncating so readers can see full post
200
201
  if (text.length > 300) {
201
- const suffix = includePermalink ? `\n\n${properties.url}` : "";
202
+ const suffix = `\n\n${properties.url}`;
202
203
  const maxLen = 300 - suffix.length - 3;
203
204
  text = text.slice(0, maxLen).trim() + "..." + suffix;
204
205
  } else if (includePermalink && !text.includes(properties.url)) {
@@ -208,6 +209,89 @@ export const getPostText = (properties, includePermalink) => {
208
209
  return text;
209
210
  };
210
211
 
212
+ /**
213
+ * Extract plain text content from properties without appending URLs
214
+ * @param {object} properties - JF2 properties
215
+ * @returns {string} Plain text content
216
+ */
217
+ export function getContentText(properties) {
218
+ if (properties.name && properties.name !== "") {
219
+ return properties.name;
220
+ }
221
+ if (properties.content?.html) {
222
+ return htmlToText(properties.content.html, {
223
+ selectors: [
224
+ { selector: "a", options: { ignoreHref: true } },
225
+ { selector: "img", format: "skip" },
226
+ ],
227
+ wordwrap: false,
228
+ });
229
+ }
230
+ if (properties.content?.text) {
231
+ return properties.content.text;
232
+ }
233
+ return "";
234
+ }
235
+
236
+ /**
237
+ * Remove a URL from text, cleaning up surrounding whitespace and prefixes
238
+ * @param {string} text - Text containing the URL
239
+ * @param {string} url - URL to remove
240
+ * @returns {string} Cleaned text
241
+ */
242
+ export function removeUrlFromText(text, url) {
243
+ if (!text || !url) return text;
244
+ let result = text.replace(url, "");
245
+ // Clean up common prefixes left behind when URL was at the end
246
+ result = result.replace(/\s*(Réf|Ref|ref|via|Via|source|Source|link|Link|→|—)\s*$/im, "");
247
+ // Clean up excessive whitespace
248
+ result = result.replace(/\n{3,}/g, "\n\n").replace(/[ \t]{2,}/g, " ").trim();
249
+ return result;
250
+ }
251
+
252
+ /**
253
+ * Build post text for Bluesky syndication with proper URL and character handling.
254
+ *
255
+ * Two modes:
256
+ * 1. External URL exists → remove from text (shown as OG card), append permalink as text
257
+ * 2. No external URL → content only (permalink shown as OG card)
258
+ *
259
+ * @param {object} properties - JF2 properties
260
+ * @param {object} [options] - Options
261
+ * @param {string} [options.externalUrl] - External URL that will be used as OG card
262
+ * @returns {string} Post text fitting within 300 chars
263
+ */
264
+ export function buildPostText(properties, options = {}) {
265
+ const { externalUrl } = options;
266
+ const LIMIT = 300;
267
+
268
+ let text = getContentText(properties);
269
+
270
+ if (externalUrl) {
271
+ // External URL will be shown as OG card — remove from text to save space
272
+ text = removeUrlFromText(text, externalUrl);
273
+
274
+ // Always append permalink to original post
275
+ const permalink = properties.url;
276
+ if (permalink) {
277
+ const suffix = `\n\n${permalink}`;
278
+ const available = LIMIT - suffix.length;
279
+ if (text.length > available) {
280
+ text = text.slice(0, available - 1).trim() + "\u2026";
281
+ }
282
+ return (text + suffix).trim();
283
+ }
284
+ }
285
+
286
+ // No external URL — permalink will be shown as OG card
287
+ // No need to duplicate it in text
288
+ if (text.length > LIMIT) {
289
+ text = text.slice(0, LIMIT - 1).trim() + "\u2026";
290
+ }
291
+
292
+ return text.trim();
293
+ }
294
+
211
295
  /**
212
296
  * Get post text for a like of an external URL
213
297
  * @param {object} properties - JF2 properties
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-syndicator-bluesky",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Bluesky syndicator for Indiekit with external like support",
5
5
  "type": "module",
6
6
  "main": "index.js",