-
Notifications
You must be signed in to change notification settings - Fork 21
Fix: Code block rendering with Shiki in dashboard editor #4017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: app
Are you sure you want to change the base?
Conversation
Fixed an issue where code blocks nested inside <CodeBlocks>/<Accordion> weren't being processed. The CodeGroup validation was too strict and would return early if any non-element children existed (like whitespace text nodes), preventing the rehype-code-block plugin from converting <pre> tags to <CodeBlock> components. Now the validation properly skips text nodes and only returns early if all children are valid <pre><code> elements, allowing nested code blocks to be rendered with Shiki syntax highlighting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
When a code block meta string looks like a filename (has a file extension), treat it as the filename= prop instead of title= prop. This fixes rendering of code blocks like: ```yaml docs.yml # content ``` Now the meta "docs.yml" will be parsed as filename="docs.yml" instead of title="docs.yml", which displays correctly in the code block header. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The dashboard was missing key rehype/remark plugins that fern-docs uses, causing issues with nested components like: - Code blocks inside Accordions/Tabs/Steps - Math equations (KaTeX) - GFM features (tables, strikethrough, etc.) - Proper frontmatter parsing Added all the missing plugins from fern-docs/bundle to match functionality: - rehypeAccordions, rehypeSteps, rehypeTabs - Structure nested content - rehypeTable, rehypeCards, rehypeButtons, rehypeParamField - Component support - remarkGfm, remarkMath, rehypeKatex - Math and GFM support - remarkFrontmatter, remarkMdxFrontmatter - Frontmatter parsing This ensures the dashboard editor renders MDX consistently with prod docs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The real fix was adding missing MDX plugins to the dashboard bundler. The shared rehype-code-block plugin works correctly and didn't need modifications.
Move 7 common rehype plugins from fern-docs/bundle to the shared @fern-docs/mdx package to enable reuse across both the docs bundle and dashboard: - rehype-accordions - rehype-buttons - rehype-cards - rehype-param-field - rehype-steps - rehype-table - rehype-tabs This fixes the dashboard build error where it was trying to import these plugins via relative paths that don't work during compilation. Changes: - Move plugin files using git mv to preserve history - Update imports to use relative paths (avoid circular deps) - Add estree-util-to-js dependency to @fern-docs/mdx - Export plugins from @fern-docs/mdx/src/plugins/index.ts - Update dashboard and docs bundle to import from shared package 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
The Pre component is no longer needed as code blocks are properly handled by the rehype-code-block plugin which transforms them to CodeBlock components during MDX processing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
c4fd1a7
to
b51de38
Compare
Add critical rehypeSlug plugin that generates IDs for headings and components like Step, Accordion, Tab, and ParamField. This matches the configuration from the main bundle and ensures proper anchor link generation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
const from = props.getPos(); | ||
const to = from + props.node.nodeSize; | ||
|
||
// Replace the content of the code block | ||
tr.setNodeMarkup(from, undefined, props.node.attrs); | ||
tr.insertText(editedCode, from + 1, to - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The handleSave
function doesn't check if getPos()
returns a valid number before using it in arithmetic operations, which can cause runtime errors when the position is undefined.
View Details
📝 Patch Details
diff --git a/packages/fern-dashboard/src/components/editor/extension-code-block/ShikiCodeBlockComponent.tsx b/packages/fern-dashboard/src/components/editor/extension-code-block/ShikiCodeBlockComponent.tsx
index 6004111bc..46431183c 100644
--- a/packages/fern-dashboard/src/components/editor/extension-code-block/ShikiCodeBlockComponent.tsx
+++ b/packages/fern-dashboard/src/components/editor/extension-code-block/ShikiCodeBlockComponent.tsx
@@ -142,6 +142,7 @@ export function ShikiCodeBlockComponent(props: ReactNodeViewProps) {
const { state } = props.editor;
const { tr } = state;
const from = props.getPos();
+ if (typeof from !== "number") return;
const to = from + props.node.nodeSize;
// Replace the content of the code block
Analysis
Missing getPos() validation in ShikiCodeBlockComponent.handleSave causes NaN position errors
What fails: ShikiCodeBlockComponent.handleSave() uses props.getPos()
directly in arithmetic operations without validation, causing NaN values when the position is undefined
How to reproduce:
// When getPos() returns undefined (when node is not in document):
const from = props.getPos(); // undefined
const to = from + props.node.nodeSize; // NaN
tr.insertText(editedCode, from + 1, to - 1); // insertText(text, NaN, NaN)
Result: ProseMirror transaction methods receive NaN positions, causing RangeError when positions are resolved. All other NodeView components in the codebase properly validate with if (typeof pos === "number")
before using position values.
Expected: Should validate position like other components: const from = props.getPos(); if (typeof from !== "number") return;
Per ProseMirror docs: "if the node is not in the document, the position returned by this function will be undefined
"
Summary
Fixes code block rendering issues in the dashboard editor by adding missing MDX plugins and improving the rehype-code-block plugin.
Problem
Code blocks in the dashboard editor were not rendering properly in nested components (Accordions, Tabs, Steps) and filenames were being parsed incorrectly.
Root Cause
The dashboard was missing key rehype/remark plugins that fern-docs uses, causing MDX content to not be processed consistently.
Changes
1. Added missing MDX plugins to dashboard bundler
2. Fixed nested code block processing
Updated the CodeGroup validation in rehype-code-block to properly skip text nodes (whitespace) instead of failing validation, allowing code blocks inside components to be processed.
3. Auto-detect filenames in code blocks
Added logic to detect when a code block meta string looks like a filename (e.g.,
docs.yml
,src/index.tsx
) and automatically use thefilename=
prop instead oftitle=
.Examples
Now these work correctly: