Skip to content

Updated script to add sub-headings to markdown code snippets#3182

Open
sacOO7 wants to merge 3 commits intomainfrom
feature/markdown-snippet-headings
Open

Updated script to add sub-headings to markdown code snippets#3182
sacOO7 wants to merge 3 commits intomainfrom
feature/markdown-snippet-headings

Conversation

@sacOO7
Copy link
Contributor

@sacOO7 sacOO7 commented Feb 5, 2026

  <Code>
  ```javascript
  code here
  ```
 
  ```realtime_java
   code here
  ```
  </Code>

Into ===>

  <Code>

  #### Javascript
  ```
  code here
  ```

  #### Realtime Java
  ```
  code here
  ```
  </Code>

Compared old and new markdown for docs/chat/rooms/messages.md using claude, it said

Old markdown:
✅ Cleaner, more concise
✅ Standard markdown syntax
⚠️ Relies on language identifiers in code fences (```javascript), which can be less prominent
⚠️ No explicit labels - must parse the code fence syntax
⚠️ Easier to miss - languages blend together without clear separators

New markdown:
✅ Explicit language headers (#### Javascript, #### React) make it immediately clear what each snippet is
✅ Hierarchical structure is unambiguous - headers act as labels
✅ Easy to extract - can search for #### pattern followed by language name
✅ Self-documenting - no need to infer from context

Summary by CodeRabbit

  • New Features

    • Code samples now include readable language subheadings (handles underscore/special variants and single-character ids).
    • New utilities improve detection and insertion of language labels and transform fenced code blocks to include those subheadings.
    • Language subheading insertion runs earlier in the processing pipeline to preserve document structure.
  • Tests

    • Expanded coverage for language labeling across many code-block scenarios and edge cases.

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Walkthrough

Adds four exported utilities to the MDX→Markdown transformer to generate language display names and inject language subheadings for multi-language code blocks; integrates subheading insertion into the conversion pipeline and extends tests to cover the new behavior.

Changes

Cohort / File(s) Summary
Transformer Implementation
data/onPostBuild/transpileMdxToMarkdown.ts
Added and exported getLanguageDisplayName, findPrecedingHeadingLevel, transformCodeBlocksWithSubheadings, and addLanguageSubheadingsToCodeBlocks. Reordered pipeline stages so language-subheading insertion runs before title prepending.
Tests
data/onPostBuild/transpileMdxToMarkdown.test.ts
Expanded tests to exercise the new exports: display-name variants, heading-level detection, code-block-to-subheading transformation, and many code-block edge cases (fenced/Code tags, language variants, whitespace, Windows EOL, multiple blocks).
Metadata / Other
manifest_file, package.json
Manifest/package metadata updates referenced in summary (lines changed noted in PR).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
I hop through fences, languages in tow,
I carve little headings where snippets grow.
From Kotlin to Swift, each name wears a crown,
No more mixed jumble — I sort them down.
A tidy trail of code for all to know.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately describes the main change: adding sub-headings to markdown code snippets, which is the core functionality added in this PR.
Linked Issues check ✅ Passed The PR implements the primary objective from DX-579: introducing explicit markdown subheadings for language-specific code snippets to improve clarity for both humans and LLMs, replacing reliance on fence labels alone.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the transformation that adds subheadings to code blocks; no unrelated modifications detected in the implementation or tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/markdown-snippet-headings

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.

@sacOO7 sacOO7 changed the title Added script to add sub-headings to generated markdown snippets Updated script to add sub-headings to markdown snippets Feb 5, 2026
@sacOO7 sacOO7 added the review-app Create a Heroku review app label Feb 5, 2026
@ably-ci ably-ci temporarily deployed to ably-docs-feature-markd-zwdofp February 5, 2026 14:13 Inactive
@sacOO7 sacOO7 force-pushed the feature/markdown-snippet-headings branch from 4215876 to 4e92bf7 Compare February 5, 2026 14:46
@ably-ci ably-ci temporarily deployed to ably-docs-feature-markd-zwdofp February 5, 2026 14:46 Inactive
@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 5, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sacOO7 sacOO7 changed the title Updated script to add sub-headings to markdown snippets Updated script to add sub-headings to markdown code snippets Feb 5, 2026
@sacOO7 sacOO7 requested a review from Copilot February 5, 2026 15:35
Copy link

@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: 3

🤖 Fix all issues with AI agents
In `@data/onPostBuild/transpileMdxToMarkdown.ts`:
- Around line 13-15: In function getLanguageDisplayName, change the early return
to use braces to satisfy the eslint "curly" rule: replace the line "if (!lang)
return '';" with a block-style conditional "if (!lang) { return ''; }" so the
function (and its parameter lang) uses a braced return before continuing to the
split/capitalize logic.
- Around line 27-31: The current codeTagRegex only matches bare
"<Code>...</Code>" so tags with attributes like "<Code fixed="true">...</Code>"
are skipped; update the codeTagRegex definition (the variable named codeTagRegex
used in the replace call) to allow an opening <Code> tag followed by optional
attributes by requiring a word boundary after "Code" and permitting any
characters except ">" until the closing ">", keep the case-insensitive and
global flags and keep the inner capture non-greedy so the replace callback (the
function handling innerContent) still receives the correct content.
- Around line 33-46: The regex in codeBlockRegex only uses \w+ and thus misses
language identifiers with hyphens, plus signs, dots, or other non-word chars;
update codeBlockRegex to capture any non-newline/backtick language token (e.g.,
/```([^\n`]+)\n[\s\S]*?```/g) and change the innerContent.replace callback
signature to accept the match and the lang (remove the redundant fullMatch
parameter) so getLanguageDisplayName(lang) receives the full language token;
modify occurrences of codeBlockRegex, innerContent.replace, and the callback
used to build transformedContent accordingly.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds language-specific subheadings to code snippets within <Code> tags in markdown documentation to make it easier for LLMs to identify which language each code snippet belongs to. The transformation converts code blocks from having implicit language identifiers in the fence syntax to explicit markdown headings.

Changes:

  • Added getLanguageDisplayName() function to convert language identifiers to display names (e.g., realtime_javascriptRealtime Javascript)
  • Added addLanguageSubheadingsToCodeBlocks() function to transform code blocks within <Code> tags by prepending language headings
  • Integrated the new transformation as Stage 12 in the MDX-to-Markdown transpilation pipeline

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
data/onPostBuild/transpileMdxToMarkdown.ts Implements two new functions for language display name formatting and code block transformation; integrates the transformation into the pipeline after template variable replacement
data/onPostBuild/transpileMdxToMarkdown.test.ts Adds comprehensive test coverage for the new functions including various language formats, multiple code blocks, and content preservation
data/onPostBuild/snapshots/transpileMdxToMarkdown.test.ts.snap Updates snapshot to reflect the new heading structure in transformed code blocks

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ably-ci ably-ci temporarily deployed to ably-docs-feature-markd-zwdofp February 6, 2026 08:40 Inactive
@sacOO7 sacOO7 marked this pull request as ready for review February 6, 2026 08:41
@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Member

@kennethkalmer kennethkalmer left a comment

Choose a reason for hiding this comment

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

I like this @sacOO7, good stuff! I have some questions though:

  1. Should we remove the language from the fenced code block for token efficiency?
  2. [nitpick] Do you think it is possible that we dynamically determine what header level the markdown should use? In messages.md we jump from ## to #### in the first block, would be nice to go to ### instead. Likewise, if we have some other page that already has #### with code snippets inside we should then be going to #####.

@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 6, 2026

@kennethkalmer good question. I had thought about both points.

  1. For the first point, I felt that removing lang identifier from fences wouldn’t make much of a difference.
  2. For the second point, I hadn’t given much thought to the specific case. I assumed that none of the sections should have #### sub-sections before snippets.

I’ll definitely double-check and update accordingly 👍

@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 6, 2026

Claude said
If LLM consumption is the primary goal, then definitely use headings. Here's why this is the right choice:
Why Headings Are Better for LLMs:

  1. Explicit Structure
    LLMs can easily query: "Extract all level-3 headings" or "Find the React code example"
  2. Clear Boundaries
    Headings create unambiguous section markers. An LLM can definitively say: "This code block belongs to the Javascript section"
  3. Semantic Meaning
    Markdown parsers and LLMs treat headings as structural elements, not just styled text
  4. Queryable
    Tools can do things like:
    "Show me all Swift examples in this document"
    "Extract code snippets organized by language"
    "Generate a language-specific version of these docs"

Also, For Pure LLM Consumption: Skip the Language Identifier
You're correct - if this markdown is primarily consumed by LLMs, adding the language identifier in code blocks is redundant and wastes tokens

@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 6, 2026

Accordingly updated the script 👍

@sacOO7 sacOO7 requested a review from Copilot February 6, 2026 15:47
@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 6, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +77 to +78
const precedingLevel = findPrecedingHeadingLevel(content, offset);
const headingPrefix = '#'.repeat(precedingLevel + 1);
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

findPrecedingHeadingLevel(content, offset) scans from the start of the document up to offset for every <Code> match, which can become O(n²) on long docs with many <Code> blocks (e.g., large API reference pages). Consider precomputing heading positions/levels once (or scanning incrementally while iterating matches) to avoid repeated full-prefix scans and substring allocations.

Copilot uses AI. Check for mistakes.
Comment on lines +770 to +774
const output = addLanguageSubheadingsToCodeBlocks(input);
expect(output).toContain('#### Javascript');
// Language should be removed from fenced code blocks
expect(output).not.toContain('```javascript');
expect(output).toContain('```\nconst x = 1;');
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

The <Code fixed="true"> test asserts that the language subheading is added, but it doesn't assert that the fixed="true" attribute is preserved in the output. Adding an expectation for the attribute would catch regressions where the transform accidentally drops <Code> tag attributes.

Copilot uses AI. Check for mistakes.
}

// Ensure proper newline after <Code> tag for markdown formatting
return `<Code>\n\n${transformedContent.trimStart()}</Code>`;
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

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

When transforming a <Code ...> block that contains language-tagged fences, the replacement hard-codes the opening tag to <Code>, which drops any existing attributes like fixed="true". This will change semantics for many docs that rely on these attributes. Preserve the original opening <Code...> tag (and ideally its exact casing/attributes) when returning the transformed block.

Copilot uses AI. Check for mistakes.
Copy link

@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: 3

🤖 Fix all issues with AI agents
In `@data/onPostBuild/transpileMdxToMarkdown.test.ts`:
- Around line 764-775: The test for addLanguageSubheadingsToCodeBlocks doesn't
assert that attributes on the <Code> tag (e.g., fixed="true") are preserved;
update the test to assert the transformed output contains '<Code fixed="true">'
and ensure the transformer (addLanguageSubheadingsToCodeBlocks / any helper used
by transpileMdxToMarkdown) is changed to retain attributes from the original
<Code> wrapper when emitting the transformed markup rather than stripping them
out; locate the logic that replaces or serializes the <Code> node in
addLanguageSubheadingsToCodeBlocks and propagate the original node.attributes
into the output so the new assertion passes.

In `@data/onPostBuild/transpileMdxToMarkdown.ts`:
- Around line 70-89: The replacement currently drops any attributes on the
original <Code> tag; update addLanguageSubheadingsToCodeBlocks to capture the
opening tag's attributes in codeTagRegex and reuse them in the returned string
so attributes like fixed="true" are preserved. Specifically, change codeTagRegex
to capture the full opening tag or its attributes (e.g.,
/<Code\b([^>]*)>([\s\S]*?)<\/Code>/gi), adjust the replacement callback
parameters to receive that attributes capture, and return
`<Code${attributes}>\n\n${transformedContent.trimStart()}</Code>` (or
equivalent) so transformCodeBlocksWithSubheadings and findPrecedingHeadingLevel
logic remain unchanged. Ensure you handle cases where there are no attributes
(attributes string may be empty) to avoid introducing extra spaces.
- Around line 28-39: findPrecedingHeadingLevel currently scans the raw content
and its headingRegex (^(#+)\s+ with /gm) can match # lines inside fenced code
blocks; use the existing splitByCodeBlocks utility to strip out fenced code
blocks before running the regex so comments inside code fences aren't treated as
headings. Concretely, keep the initial contentBefore = content.substring(0,
position) but call splitByCodeBlocks(contentBefore) (assign to
contentWithoutCodeBlocks) and run headingRegex.exec against
contentWithoutCodeBlocks instead of contentBefore; ensure you still default
lastHeadingLevel = 3 and update lastHeadingLevel from match[1].length as before.
🧹 Nitpick comments (1)
data/onPostBuild/transpileMdxToMarkdown.ts (1)

46-62: Consider trimming the captured language identifier.

The regex ([^\n]+)can capture trailing whitespace if a fence line has it (e.g., `` ```javascript ``). This would propagate into the heading as#### Javascript . Adding a .trim()` is a low-cost defensive measure.

Suggested tweak
   return innerContent.replace(codeBlockRegex, (_codeBlock, lang, codeContent) => {
-    const displayName = getLanguageDisplayName(lang);
+    const displayName = getLanguageDisplayName(lang.trim());
     return `${headingPrefix} ${displayName}\n\n\`\`\`\n${codeContent}\`\`\``;
   });

Comment on lines +764 to +775
it('should handle <Code> tags with attributes like fixed="true"', () => {
const input = `<Code fixed="true">
\`\`\`javascript
const x = 1;
\`\`\`
</Code>`;
const output = addLanguageSubheadingsToCodeBlocks(input);
expect(output).toContain('#### Javascript');
// Language should be removed from fenced code blocks
expect(output).not.toContain('```javascript');
expect(output).toContain('```\nconst x = 1;');
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Test doesn't verify that fixed="true" is preserved in the output.

The test is titled "should handle <Code> tags with attributes" but only asserts on the heading and language removal — it never checks whether the attribute survives the transformation. This aligns with the attribute-stripping bug flagged in the source file. Once that's fixed, add an assertion:

     const output = addLanguageSubheadingsToCodeBlocks(input);
     expect(output).toContain('#### Javascript');
     // Language should be removed from fenced code blocks
     expect(output).not.toContain('```javascript');
     expect(output).toContain('```\nconst x = 1;');
+    // Attributes on <Code> tag should be preserved
+    expect(output).toContain('<Code fixed="true">');
🤖 Prompt for AI Agents
In `@data/onPostBuild/transpileMdxToMarkdown.test.ts` around lines 764 - 775, The
test for addLanguageSubheadingsToCodeBlocks doesn't assert that attributes on
the <Code> tag (e.g., fixed="true") are preserved; update the test to assert the
transformed output contains '<Code fixed="true">' and ensure the transformer
(addLanguageSubheadingsToCodeBlocks / any helper used by transpileMdxToMarkdown)
is changed to retain attributes from the original <Code> wrapper when emitting
the transformed markup rather than stripping them out; locate the logic that
replaces or serializes the <Code> node in addLanguageSubheadingsToCodeBlocks and
propagate the original node.attributes into the output so the new assertion
passes.

Comment on lines +28 to +39
function findPrecedingHeadingLevel(content: string, position: number): number {
const contentBefore = content.substring(0, position);
const headingRegex = /^(#+)\s+/gm;
let lastHeadingLevel = 3; // Defaults to 3 when no heading is found
let match;

while ((match = headingRegex.exec(contentBefore)) !== null) {
lastHeadingLevel = match[1].length;
}

return lastHeadingLevel;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

findPrecedingHeadingLevel can match # lines inside fenced code blocks.

The regex ^(#+)\s+ with the gm flag will match comment lines inside fenced code blocks that start with # (e.g., Python # some comment, shell #!/bin/bash). Since this function is called on the full document content, a # comment line in a preceding code block could be misidentified as an h1 heading, yielding an h2 subheading instead of the expected h4.

The existing splitByCodeBlocks utility could be leveraged to strip fenced code block content before scanning for headings.

Suggested approach
 function findPrecedingHeadingLevel(content: string, position: number): number {
   const contentBefore = content.substring(0, position);
-  const headingRegex = /^(#+)\s+/gm;
+  // Strip fenced code block contents so # comment lines inside them aren't misidentified as headings
+  const contentWithoutCodeBlocks = contentBefore.replace(/```[\s\S]*?```/g, '');
+  const headingRegex = /^(#+)\s+/gm;
   let lastHeadingLevel = 3; // Defaults to 3 when no heading is found
   let match;

-  while ((match = headingRegex.exec(contentBefore)) !== null) {
+  while ((match = headingRegex.exec(contentWithoutCodeBlocks)) !== null) {
     lastHeadingLevel = match[1].length;
   }

   return lastHeadingLevel;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function findPrecedingHeadingLevel(content: string, position: number): number {
const contentBefore = content.substring(0, position);
const headingRegex = /^(#+)\s+/gm;
let lastHeadingLevel = 3; // Defaults to 3 when no heading is found
let match;
while ((match = headingRegex.exec(contentBefore)) !== null) {
lastHeadingLevel = match[1].length;
}
return lastHeadingLevel;
}
function findPrecedingHeadingLevel(content: string, position: number): number {
const contentBefore = content.substring(0, position);
// Strip fenced code block contents so # comment lines inside them aren't misidentified as headings
const contentWithoutCodeBlocks = contentBefore.replace(/
🤖 Prompt for AI Agents
In `@data/onPostBuild/transpileMdxToMarkdown.ts` around lines 28 - 39,
findPrecedingHeadingLevel currently scans the raw content and its headingRegex
(^(#+)\s+ with /gm) can match # lines inside fenced code blocks; use the
existing splitByCodeBlocks utility to strip out fenced code blocks before
running the regex so comments inside code fences aren't treated as headings.
Concretely, keep the initial contentBefore = content.substring(0, position) but
call splitByCodeBlocks(contentBefore) (assign to contentWithoutCodeBlocks) and
run headingRegex.exec against contentWithoutCodeBlocks instead of contentBefore;
ensure you still default lastHeadingLevel = 3 and update lastHeadingLevel from
match[1].length as before.

Comment on lines +70 to +89
function addLanguageSubheadingsToCodeBlocks(content: string): string {
// Match <Code> blocks with optional attributes (case-insensitive for the tag)
// Handles both <Code> and <Code fixed="true"> etc.
const codeTagRegex = /<Code\b[^>]*>([\s\S]*?)<\/Code>/gi;

return content.replace(codeTagRegex, (fullMatch, innerContent: string, offset: number) => {
// Calculate the appropriate heading level based on preceding headings
const precedingLevel = findPrecedingHeadingLevel(content, offset);
const headingPrefix = '#'.repeat(precedingLevel + 1);

// Transform code blocks with subheadings
const transformedContent = transformCodeBlocksWithSubheadings(innerContent, headingPrefix);
if (transformedContent === null) {
return fullMatch; // No code blocks with language - return unchanged
}

// Ensure proper newline after <Code> tag for markdown formatting
return `<Code>\n\n${transformedContent.trimStart()}</Code>`;
});
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

<Code> tag attributes (e.g., fixed="true") are silently stripped.

Line 87 always emits a bare <Code> tag, discarding any attributes from the original opening tag. For example, <Code fixed="true"> becomes <Code> after transformation. This alters the semantics of the output.

Capture the attributes in the regex and reuse them in the replacement:

Proposed fix
 function addLanguageSubheadingsToCodeBlocks(content: string): string {
-  const codeTagRegex = /<Code\b[^>]*>([\s\S]*?)<\/Code>/gi;
+  const codeTagRegex = /<Code\b([^>]*)>([\s\S]*?)<\/Code>/gi;

-  return content.replace(codeTagRegex, (fullMatch, innerContent: string, offset: number) => {
+  return content.replace(codeTagRegex, (fullMatch, attributes: string, innerContent: string, offset: number) => {
     const precedingLevel = findPrecedingHeadingLevel(content, offset);
     const headingPrefix = '#'.repeat(precedingLevel + 1);

     const transformedContent = transformCodeBlocksWithSubheadings(innerContent, headingPrefix);
     if (transformedContent === null) {
       return fullMatch;
     }

-    return `<Code>\n\n${transformedContent.trimStart()}</Code>`;
+    return `<Code${attributes}>\n\n${transformedContent.trimStart()}</Code>`;
   });
 }
🤖 Prompt for AI Agents
In `@data/onPostBuild/transpileMdxToMarkdown.ts` around lines 70 - 89, The
replacement currently drops any attributes on the original <Code> tag; update
addLanguageSubheadingsToCodeBlocks to capture the opening tag's attributes in
codeTagRegex and reuse them in the returned string so attributes like
fixed="true" are preserved. Specifically, change codeTagRegex to capture the
full opening tag or its attributes (e.g.,
/<Code\b([^>]*)>([\s\S]*?)<\/Code>/gi), adjust the replacement callback
parameters to receive that attributes capture, and return
`<Code${attributes}>\n\n${transformedContent.trimStart()}</Code>` (or
equivalent) so transformCodeBlocksWithSubheadings and findPrecedingHeadingLevel
logic remain unchanged. Ensure you handle cases where there are no attributes
(attributes string may be empty) to avoid introducing extra spaces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-app Create a Heroku review app

Development

Successfully merging this pull request may close these issues.

3 participants