Skip to content

fix: preserve code blocks in embedded markdown files (fixes #106)#122

Merged
bingryan merged 1 commit intomasterfrom
bingryan/analyze-issue-106
Jan 3, 2026
Merged

fix: preserve code blocks in embedded markdown files (fixes #106)#122
bingryan merged 1 commit intomasterfrom
bingryan/analyze-issue-106

Conversation

@bingryan
Copy link
Copy Markdown
Owner

@bingryan bingryan commented Jan 3, 2026

Summary

Changes

  • Rewrote getEmbedMap() function to read raw markdown from source files
  • Added getHeadingContent() helper to extract heading sections
  • Added getEmbedContentFromSource() helper to unify embed content extraction
  • Maintains support for:
    • ![[file.md]] - full file embeds
    • ![[file.md#heading]] - heading section embeds
    • ![[file.md#^blockid]] - block reference embeds
  • Preserves removeYamlHeader setting behavior

Test plan

  • Test embedding a file containing mermaid code blocks
  • Verify mermaid code is preserved in exported markdown (not converted to HTML)
  • Test heading-based embeds (![[file.md#heading]])
  • Test block reference embeds (![[file.md#^blockid]])
  • Verify removeYamlHeader setting still works correctly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Support embedding specific blocks and headings, plus inline block embeds when needed.
    • Configurable option to remove YAML headers from embedded content.
  • Bug Fixes

    • More reliable embed resolution by extracting content from source text instead of rendered output.
    • Improved error handling and safer fallback behavior for unresolved embeds.
  • Refactor

    • Internal improvements to embed parsing and content normalization.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 3, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Refactors embed resolution in src/utils.ts to extract embed content directly from raw markdown (headings, blocks, whole files) using new helpers, with YAML-header removal support and safer error handling returning null on failures.

Changes

Cohort / File(s) Summary
Embed resolution and helpers
src/utils.ts
Replaces HTML-based embed parsing with source-based extraction; adds getHeadingContent, getEmbedContentFromSource, getBlockContent, and parseEmbedLink; reworks getEmbedMap to key by embed link and store quoted markdown content; supports block/heading/file embeds, optional YAML header stripping via plugin.settings.removeYamlHeader, and logs errors returning null instead of throwing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble lines from markdown beds,
Pull out headings, blocks, and threads,
Wrap them in quotes, neat and bright,
Hopping through code in morning light —
A tiny rabbit, binding embeds 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: preserve code blocks in embedded markdown files (fixes #106)' directly describes the main change: switching from DOM-based embed extraction to raw markdown parsing to preserve code block syntax in embeds, which is the primary objective of the PR.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a000fb and 20597d1.

📒 Files selected for processing (1)
  • src/utils.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Previously, embedded content was extracted from rendered HTML which
converted code blocks (like mermaid) to their rendered form. This
made it impossible to preserve the original code block syntax.

Now reads raw markdown directly from source files, preserving all
code blocks in their original format.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bingryan bingryan force-pushed the bingryan/analyze-issue-106 branch from 7a000fb to 20597d1 Compare January 3, 2026 13:44
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/utils.ts (1)

4-4: Remove unused import.

The htmlToMarkdown import is no longer used after refactoring to read raw markdown directly instead of parsing HTML DOM.

🔎 Proposed fix
-import { TAbstractFile, TFile, TFolder, htmlToMarkdown, CachedMetadata } from "obsidian";
+import { TAbstractFile, TFile, TFolder, CachedMetadata } from "obsidian";
🧹 Nitpick comments (1)
src/utils.ts (1)

515-575: Consider extracting YAML header removal logic.

The YAML header removal logic is duplicated in three places (lines 546-548, 559-561, 566-568). While this works correctly, you could extract it to reduce duplication.

🔎 Optional refactor to deduplicate YAML header removal
 async function getEmbedContentFromSource(
     plugin: MarkdownExportPlugin,
     embedLink: string,
     currentPath: string
 ): Promise<string | null> {
     const parsed = parseEmbedLink(embedLink, currentPath);
 
     if (!parsed.filePath) {
         return null;
     }
 
     try {
         const file = plugin.app.vault.getAbstractFileByPath(parsed.filePath);
         if (!(file instanceof TFile)) {
             return null;
         }
 
         let content = await plugin.app.vault.cachedRead(file);
 
         // Extract specific section if needed
         if (parsed.blockId) {
             const blockContent = await getBlockContent(
                 plugin,
                 parsed.filePath,
                 parsed.blockId
             );
-            // Remove YAML header if configured (in case block is at start of file)
-            if (plugin.settings.removeYamlHeader && blockContent) {
-                return blockContent.replace(EMBED_METADATA_REGEXP, "");
-            }
-            return blockContent;
+            content = blockContent;
         }
-
-        if (parsed.heading) {
+        else if (parsed.heading) {
             const headingContent = await getHeadingContent(
                 plugin,
                 parsed.filePath,
                 parsed.heading
             );
-            // Remove YAML header if configured (in case heading is at start of file)
-            if (plugin.settings.removeYamlHeader && headingContent) {
-                return headingContent.replace(EMBED_METADATA_REGEXP, "");
-            }
-            return headingContent;
+            content = headingContent;
         }
 
         // Remove YAML header if configured for full file content
-        if (plugin.settings.removeYamlHeader) {
+        if (content && plugin.settings.removeYamlHeader) {
             content = content.replace(EMBED_METADATA_REGEXP, "");
         }
 
         return content;
     } catch (error) {
         console.error("Error getting embed content from source:", error);
         return null;
     }
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 375f99c and 7a000fb.

📒 Files selected for processing (1)
  • src/utils.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/utils.ts (1)
src/config.ts (1)
  • EMBED_METADATA_REGEXP (8-9)
🪛 GitHub Actions: Lint and build
src/utils.ts

[error] 4-4: ESLint: 'htmlToMarkdown' is defined but never used. (no-unused-vars) | Command: eslint src/ --ext .js,.jsx,.ts,.tsx

🪛 GitHub Check: build_and_test (18)
src/utils.ts

[failure] 597-597:
'embedValue' is never reassigned. Use 'const' instead

🪛 GitHub Check: build_and_test (20)
src/utils.ts

[failure] 597-597:
'embedValue' is never reassigned. Use 'const' instead

🔇 Additional comments (2)
src/utils.ts (2)

452-513: LGTM: Heading extraction logic is sound.

The function correctly identifies headings, extracts content up to the next heading of equal or higher level, and handles errors gracefully by returning null.


660-715: The path resolution for root-level files is already correctly handled. The code uses ternary operators that properly evaluate empty currentDir strings (which result from substring(0, -1) when files have no directory separator), falling back to use the link text directly. No issues found.

@bingryan bingryan merged commit 45829d2 into master Jan 3, 2026
4 of 5 checks passed
@bingryan bingryan deleted the bingryan/analyze-issue-106 branch January 3, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant