Skip to content

Feature Request: Inner Content Editing via Raw Markdown #505

Open
@gurkerl83

Description

@gurkerl83

Feature Request: Inner Content Editing via Raw Markdown

Context

In CodeHike’s current setup, each block (or “section”) renders Markdown content directly through CodeHike’s internal transformations. While this works well for static display, there’s no straightforward way to preserve the original Markdown for subsequent edits. As a result, user modifications often require extracting HTML from a rendered React node—assigning the content to the DOM and then reading it back—just to re-generate the original Markdown. This process is tedious and error-prone.

To streamline round-trip editing (Markdown → Editor → Markdown), we need to keep the original Markdown intact. This ensures that specialized MDX syntax and custom transformations don’t get lost or mangled in the process.


Goal

Store and expose the raw Markdown for each block so that:

  1. We can render it through CodeHike’s existing pipelines.
  2. We can edit the content in a rich-text editor or other tooling that supports Markdown.
  3. We preserve all MDX or custom syntax details between edits.

Proposed Change

We added a dedicated body property in the _data object of each HikeSection. This field captures the raw Markdown for that section, simplifying future transformations and allowing for easy re-injection into editors.

const root: HikeSection = {
  type: 'section',
  _data: {
    header: '',
    body: '' // ← new field for the raw Markdown
  },
  name: '',
  depth: 0,
  title: '',
  parent: null,
  children: [],
  multi: false
};

Implementation in listToSection.ts
Below is a brief look at the strategy we used in listToSection.ts to populate the _data.body field:

function pushToSection(parent: HikeSection, node: any) {
  if (!parent._dataNodes) {
    parent._dataNodes = [];
  }
  parent._dataNodes.push(node);

  parent.children.push({
    type: 'content',
    value: node
  });
}

function finalizeSections(section: HikeSection) {
  if (section._dataNodes && section._dataNodes.length > 0) {
    const miniRoot = {
      type: 'root' as const,
      children: section._dataNodes
    };
    const markdown = toMarkdown(miniRoot).replace(/\n(?!\n)/g, '');
    section._data.body = markdown;
  }
  for (const child of section.children) {
    if (child.type === 'section') {
      finalizeSections(child);
    }
  }
}

Parse & Collect
As we traverse the AST, we store each node in an intermediate array (_dataNodes) instead of immediately transforming it to Markdown.

Finalize
After building the entire hierarchy, we call a finalizeSections function on each HikeSection. This function wraps _dataNodes in a mini “root” node, converts them to a Markdown string via mdast-util-to-markdown, and stores the result in _data.body. By isolating the raw content capture from the parsing logic, we avoid conflicts with specialized nodes and maintain the section’s original text.


Observed Complexities
In standard Markdown scenarios, storing the raw content via _data.body works smoothly. However, in more advanced CodeHike demos (e.g., BlocksDemo), we’ve seen issues arising from special or custom MDX node types such as mdxFlowExpression. Because these nodes aren’t handled by default Markdown utilities, they may introduce parse errors or unexpected behavior. A separate, MDX-aware plugin or additional parsing configuration could be required to handle these cases gracefully.


Example Use Case
Below is an example of a more complex MDX snippet, which we’d like to make editable without losing any of its structure or syntax:

<TimelineInjector>

# !!posts Post 1

Nestled **between** turquoise waters and majestic peaks, this enchanting haven offers breathtaking landscapes and endless adventure. Every moment spent here feels like a vibrant dream come true.

Wander through charming streets lined with colorful murals and delightful cafés, where every corner tells a story. The warm, welcoming community makes you feel like a part of the magic that surrounds this place.

# !!posts Post 2

Paradise.

Sunsets paint the sky in hues of wonder.

Enchanted.

</TimelineInjector>

By populating _data.body with this exact Markdown (including any custom CodeHike directives or MDX features), we can:

  1. Render the snippet in CodeHike.
  2. Open it in a rich-text or Markdown-focused editor without losing the specialized sections and flow elements.
  3. Preserve and re-export all syntax details as new edits are made.

Conclusion
Storing the original Markdown in _data.body provides a clean “exit strategy” for editing workflows. Rather than reconstructing Markdown from the rendered DOM, we maintain the source text directly, ensuring round-trip integrity—even for advanced MDX content. This approach simplifies edits, retains specialized syntax, and avoids parse errors or data loss. It works well in most standard scenarios and, with some additional handling for more complex MDX nodes, can support a full range of CodeHike use cases.

Thx!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions