Structure intrinsic MDX elements in Studio#155
Conversation
📝 WalkthroughWalkthroughThis PR adds structured block support for lowercase MDX/HTML intrinsic elements (e.g., ChangesMDX Intrinsic Element Architecture
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/studio/src/lib/runtime-ui/components/editor/mdx-props-panel.tsx (1)
27-29: ⚡ Quick winMake
kindrequired onMdxPropsPanelSelection.Keeping
kindoptional allows intrinsic selections to silently fall into the non-intrinsic path if a caller forgets to pass it.🧩 Suggested type tightening
export type MdxPropsPanelSelection = { - kind?: "component" | "intrinsic"; + kind: "component" | "intrinsic"; component: MdxCatalogComponent | undefined; componentName: string; isVoid: boolean;As per coding guidelines, "Use TypeScript in strict mode with
nodenextmodule resolution andcompositeprojects enabled".Also applies to: 60-82
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/studio/src/lib/runtime-ui/components/editor/mdx-props-panel.tsx` around lines 27 - 29, The MdxPropsPanelSelection type currently has an optional kind which can cause intrinsic selections to be misrouted; make kind required by changing its declaration in the MdxPropsPanelSelection type (the symbol to edit is MdxPropsPanelSelection with fields kind and component) so kind is not optional, then update any usages that construct or narrow MdxPropsPanelSelection (e.g., code paths referenced around the selection handling logic) to always supply a valid "component" or "intrinsic" value for kind or adjust callers to pass the correct discriminant; ensure related type checks and switch/case branches rely on the now-required discriminant.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.ai/plans/2026-05-28-structured-intrinsic-mdx-elements.md:
- Line 13: The heading "### Task 1: Parser Contract Tests" jumps from an H1 to
H3 causing MD001; change that heading to an H2 by replacing "### Task 1: Parser
Contract Tests" with "## Task 1: Parser Contract Tests" (or alternatively add a
parent H2 above it), ensuring the heading level sequence is H1 → H2 → H3 for
downstream sections.
In `@docs/specs/SPEC-007-editor-mdx-and-collaboration.md`:
- Around line 259-276: The spec contains a contract conflict between the
"Lowercase intrinsic MDX/HTML elements" section (which treats parseable
lowercase HTML as structured intrinsic blocks) and the "Children / Nested
Content" section (which claims lowercase HTML remains raw-island content); pick
and document a single behavior and update the "Children / Nested Content" text
to match: declare that parseable lowercase intrinsic elements (e.g., div, form,
label, input, button) are parsed into intrinsic blocks with props/void/editable
children and only unparseable/unsupported syntax is preserved as a raw MDX
island; make sure to reference the terms "intrinsic blocks" and "raw MDX
islands" in the revised paragraph so implementers know which path to follow.
In
`@packages/studio/src/lib/runtime-ui/components/editor/mdx-intrinsic-element-node-view.tsx`:
- Around line 22-30: Update the intrinsic preview sanitization to block
URL-based resource loads: add URL-bearing attribute names such as "srcset" (and
any case-insensitive variants you check) to the URL_BEARING_ATTRIBUTES set and
ensure the attribute-checking logic (the code that iterates attributes for MDX
intrinsic nodes) rejects values that contain URL patterns (e.g., /url\s*\(/i or
value starting with "http:", "https:", "//", or data: for non-allowed types) as
well as disallowing style attributes that include url(...) by treating "style"
as unsafe or by explicitly scanning style values for url(...) and rejecting
them; also keep UNSAFE_ATTRIBUTES (like "srcdoc" and "dangerouslySetInnerHTML")
intact and apply the same case-insensitive checks in the other attribute-filter
locations referenced around the 90-119 and 121-125 ranges so all attribute
filters use the updated URL_BEARING_ATTRIBUTES and the url(...) / protocol
checks.
---
Nitpick comments:
In `@packages/studio/src/lib/runtime-ui/components/editor/mdx-props-panel.tsx`:
- Around line 27-29: The MdxPropsPanelSelection type currently has an optional
kind which can cause intrinsic selections to be misrouted; make kind required by
changing its declaration in the MdxPropsPanelSelection type (the symbol to edit
is MdxPropsPanelSelection with fields kind and component) so kind is not
optional, then update any usages that construct or narrow MdxPropsPanelSelection
(e.g., code paths referenced around the selection handling logic) to always
supply a valid "component" or "intrinsic" value for kind or adjust callers to
pass the correct discriminant; ensure related type checks and switch/case
branches rely on the now-required discriminant.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6ff9c1b1-655c-4b07-b202-c16e92173167
📒 Files selected for processing (18)
.ai/plans/2026-05-28-structured-intrinsic-mdx-elements.md.ai/research/2026-05-28-structured-intrinsic-mdx-elements-design.md.changeset/silly-months-kneel.mddocs/specs/SPEC-007-editor-mdx-and-collaboration.mddocs/specs/SPEC-014-ai-assisted-studio-editing.mdpackages/studio/src/lib/editor-extensions.tspackages/studio/src/lib/html-void-elements.tspackages/studio/src/lib/markdown-pipeline.test.tspackages/studio/src/lib/mdx-intrinsic-element-extension.tspackages/studio/src/lib/mdx-markdown-parser.tspackages/studio/src/lib/runtime-ui/components/editor/mdx-component-panel-selection.test.tspackages/studio/src/lib/runtime-ui/components/editor/mdx-component-panel-selection.tspackages/studio/src/lib/runtime-ui/components/editor/mdx-component-selection.test.tspackages/studio/src/lib/runtime-ui/components/editor/mdx-component-selection.tspackages/studio/src/lib/runtime-ui/components/editor/mdx-intrinsic-element-node-view.tsxpackages/studio/src/lib/runtime-ui/components/editor/mdx-props-panel.tsxpackages/studio/src/lib/runtime-ui/components/editor/tiptap-editor.tsxpackages/studio/src/lib/runtime-ui/components/editor/visual-style-inspector.tsx
|
|
||
| --- | ||
|
|
||
| ### Task 1: Parser Contract Tests |
There was a problem hiding this comment.
Fix heading level jump to keep markdown lint-clean.
Line 13 jumps from an H1 section to H3 (###) without an intermediate H2, which triggers MD001. Change it to ## (or insert a missing ## parent heading).
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 13-13: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.ai/plans/2026-05-28-structured-intrinsic-mdx-elements.md at line 13, The
heading "### Task 1: Parser Contract Tests" jumps from an H1 to H3 causing
MD001; change that heading to an H2 by replacing "### Task 1: Parser Contract
Tests" with "## Task 1: Parser Contract Tests" (or alternatively add a parent H2
above it), ensuring the heading level sequence is H1 → H2 → H3 for downstream
sections.
| Lowercase intrinsic MDX/HTML elements such as `<div>`, `<form>`, `<label>`, | ||
| `<input>`, and `<button>` remain valid advanced MDX authoring syntax. When their | ||
| attributes can be represented as Studio props, Studio parses them into | ||
| first-class visual composition blocks with their tag name, props, void status, | ||
| and editable children. Intrinsic blocks are not catalog components, are not | ||
| shown in the insert component palette by default, and do not participate in host | ||
| component prop-schema validation. They still render in the canvas with the same | ||
| block affordances as MDX components so editors never see a partial raw HTML | ||
| preview as the normal editing surface. | ||
|
|
||
| Studio preserves intrinsic HTML semantics during serialization: an intrinsic | ||
| `div` block serializes as `<div>`, a `form` block serializes as `<form>`, and | ||
| void intrinsic elements such as `<input />` serialize as self-closing lowercase | ||
| HTML. Built-ins are the supported visual-editing primitives for common layout | ||
| and inline content, while intrinsic blocks cover valid native HTML semantics and | ||
| legacy MDX content. Raw MDX islands are a last-resort preservation fallback only | ||
| for unparseable or unsupported syntax; they must be clearly labeled as | ||
| unsupported content rather than rendered as a half-raw inline preview. |
There was a problem hiding this comment.
Resolve contract conflict on lowercase intrinsic handling.
This new section says parseable lowercase intrinsic HTML should be structured blocks, but the later “Children / Nested Content” section still states raw lowercase HTML remains raw-island content. Please align both sections to one behavior to avoid conflicting implementation guidance.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/specs/SPEC-007-editor-mdx-and-collaboration.md` around lines 259 - 276,
The spec contains a contract conflict between the "Lowercase intrinsic MDX/HTML
elements" section (which treats parseable lowercase HTML as structured intrinsic
blocks) and the "Children / Nested Content" section (which claims lowercase HTML
remains raw-island content); pick and document a single behavior and update the
"Children / Nested Content" text to match: declare that parseable lowercase
intrinsic elements (e.g., div, form, label, input, button) are parsed into
intrinsic blocks with props/void/editable children and only
unparseable/unsupported syntax is preserved as a raw MDX island; make sure to
reference the terms "intrinsic blocks" and "raw MDX islands" in the revised
paragraph so implementers know which path to follow.
| const URL_BEARING_ATTRIBUTES = new Set([ | ||
| "action", | ||
| "formaction", | ||
| "href", | ||
| "poster", | ||
| "src", | ||
| "xlinkhref", | ||
| ]); | ||
| const UNSAFE_ATTRIBUTES = new Set(["srcdoc", "dangerouslysetinnerhtml"]); |
There was a problem hiding this comment.
Harden intrinsic preview sanitization against URL-based resource loads.
The current filter still allows URL-bearing inputs (e.g., srcSet) and style values containing url(...), which can trigger unintended external requests when previewing user-authored content.
🔒 Proposed hardening
const URL_BEARING_ATTRIBUTES = new Set([
"action",
+ "background",
+ "cite",
+ "data",
"formaction",
"href",
+ "ping",
"poster",
"src",
+ "srcset",
+ "usemap",
"xlinkhref",
]);
+
+function hasCssUrl(value: string): boolean {
+ return /\burl\s*\(/i.test(value);
+}
@@
if (name === "style") {
if (isFlatStyleRecord(value)) {
- safeProps[name] = value;
+ const sanitizedStyle = Object.fromEntries(
+ Object.entries(value).filter(([, entry]) => {
+ return typeof entry === "number" || !hasCssUrl(entry);
+ }),
+ );
+ if (Object.keys(sanitizedStyle).length > 0) {
+ safeProps[name] = sanitizedStyle;
+ }
}
continue;
}Also applies to: 90-119, 121-125
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@packages/studio/src/lib/runtime-ui/components/editor/mdx-intrinsic-element-node-view.tsx`
around lines 22 - 30, Update the intrinsic preview sanitization to block
URL-based resource loads: add URL-bearing attribute names such as "srcset" (and
any case-insensitive variants you check) to the URL_BEARING_ATTRIBUTES set and
ensure the attribute-checking logic (the code that iterates attributes for MDX
intrinsic nodes) rejects values that contain URL patterns (e.g., /url\s*\(/i or
value starting with "http:", "https:", "//", or data: for non-allowed types) as
well as disallowing style attributes that include url(...) by treating "style"
as unsafe or by explicitly scanning style values for url(...) and rejecting
them; also keep UNSAFE_ATTRIBUTES (like "srcdoc" and "dangerouslySetInnerHTML")
intact and apply the same case-insensitive checks in the other attribute-filter
locations referenced around the 90-119 and 121-125 ranges so all attribute
filters use the updated URL_BEARING_ATTRIBUTES and the url(...) / protocol
checks.
Summary
Validation
Summary by CodeRabbit
<div>,<form>,<input>,<button>, etc.) are now rendered as structured, editable blocks in the editor.