Skip to content

Commit aa70675

Browse files
committed
feat(notion-fetch): add workaround for image hyperlinks via captions
Since Notion's "Add link" menu option doesn't expose hyperlinks via the API, implement a workaround that detects URLs in image captions: Method 1: Link annotations in caption rich_text - Detects when URLs are formatted as links in captions - Uses the link.url property from the rich_text annotation Method 2: Plain text URL detection (NEW) - Detects URLs typed as plain text in captions - Uses regex to extract http(s) URLs: /https?:\/\/[^\s]+/ - Separates URL from alt text Method 3 & 4: Future-proofing - Checks for image.link and block.link properties - Will work if Notion adds API support in the future Users can now make images clickable by: 1. Typing a URL in the image caption (preferred workaround) 2. Pasting a link in the caption (Notion converts it automatically) Note: The Notion UI "Add link" feature is not supported by the API. Images using that feature will not have clickable links in the output. Related to #96
1 parent 198da20 commit aa70675

File tree

1 file changed

+35
-19
lines changed

1 file changed

+35
-19
lines changed

scripts/notionClient.ts

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -347,14 +347,6 @@ const imageTransformer: BlockToMarkdown = async (block) => {
347347
return "";
348348
}
349349

350-
// DEBUG: Log the complete image block structure to understand hyperlinks
351-
if (!IS_TEST_ENV) {
352-
console.log(
353-
chalk.gray("DEBUG: Image block structure:"),
354-
JSON.stringify(imageBlock, null, 2)
355-
);
356-
}
357-
358350
// Get image URL from external or file
359351
const imageUrl = image.external?.url || image.file?.url || image.url || "";
360352

@@ -363,43 +355,67 @@ const imageTransformer: BlockToMarkdown = async (block) => {
363355
}
364356

365357
// Check if image has a hyperlink
366-
// In Notion, hyperlinks are typically stored in the caption as a link annotation
358+
// WORKAROUND: Since Notion's "Add link" feature doesn't expose links via the API,
359+
// we detect URLs in captions as an alternative approach
367360
let linkUrl = "";
368361
let altText = "";
369362

370-
// Check for links in caption rich_text first
363+
// Method 1: Check for links in caption rich_text (when URL is formatted as a link)
371364
if (image.caption && Array.isArray(image.caption)) {
372365
for (const captionItem of image.caption) {
373-
// Check if this caption item is a link
366+
// Check if this caption item has a link annotation
374367
if (captionItem.type === "text" && captionItem.text?.link?.url) {
375368
linkUrl = captionItem.text.link.url;
376-
console.log(chalk.green(`Found link in caption: ${linkUrl}`));
377-
// Don't use the linked text as alt text
369+
if (!IS_TEST_ENV) {
370+
console.log(chalk.green(`✓ Found link in caption: ${linkUrl}`));
371+
}
372+
// Don't use the linked text as alt text - it's the URL destination
378373
break;
379374
} else if (captionItem.plain_text && !linkUrl) {
380375
// Use non-linked caption text as alt text
381376
altText += captionItem.plain_text || "";
382377
}
383378
}
384379

385-
// If no link was found, use the full caption as alt text
380+
// Method 2: Check for plain text URLs in caption (fallback)
381+
// This catches cases where users type URLs without Notion converting them
386382
if (!linkUrl) {
387-
altText = image.caption
383+
const fullCaption = image.caption
388384
.map((item: any) => item.plain_text || "")
389385
.join("");
386+
387+
// Simple URL regex to detect http(s) URLs
388+
const urlMatch = fullCaption.match(/https?:\/\/[^\s]+/);
389+
if (urlMatch) {
390+
linkUrl = urlMatch[0];
391+
if (!IS_TEST_ENV) {
392+
console.log(
393+
chalk.green(`✓ Found plain text URL in caption: ${linkUrl}`)
394+
);
395+
}
396+
// Use the rest of the caption as alt text
397+
altText = fullCaption.replace(linkUrl, "").trim();
398+
} else {
399+
// No URL found, use full caption as alt text
400+
altText = fullCaption;
401+
}
390402
}
391403
}
392404

393-
// Check for dedicated link property on the image object (fallback)
405+
// Method 3: Check for dedicated link property on the image object (API support if added)
394406
if (!linkUrl && image.link) {
395407
linkUrl = image.link;
396-
console.log(chalk.green(`Found image link property: ${linkUrl}`));
408+
if (!IS_TEST_ENV) {
409+
console.log(chalk.green(`✓ Found image link property: ${linkUrl}`));
410+
}
397411
}
398412

399-
// Check for link on the block level (fallback)
413+
// Method 4: Check for link on the block level (API support if added)
400414
if (!linkUrl && imageBlock.link) {
401415
linkUrl = imageBlock.link;
402-
console.log(chalk.green(`Found block-level link: ${linkUrl}`));
416+
if (!IS_TEST_ENV) {
417+
console.log(chalk.green(`✓ Found block-level link: ${linkUrl}`));
418+
}
403419
}
404420

405421
// Generate markdown

0 commit comments

Comments
 (0)