-
Notifications
You must be signed in to change notification settings - Fork 627
WIP: add symbol name serialization and inputs to level props #1787
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: main
Are you sure you want to change the base?
Conversation
|
|
View your CI Pipeline Execution ↗ for commit 5c65b84
☁️ Nx Cloud last updated this comment at |
liamdebeasi
left a comment
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.
I'm curious to hear how your testing in Editor AI went today!
| * - Adds "Symbol" prefix to avoid collisions | ||
| * - Returns "Symbol" if no valid name can be generated | ||
| */ | ||
| const sanitizeSymbolName = (name: string | undefined): string => { |
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.
this may not actually be true, but I thought symbols always had names so this param would never be undefined?
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.
Not sure if there are any edge cases so I went with defense-mode. All symbols, in theory, should have names
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.
sounds good. since symbols should always have a name we can probably remove any undefined handling
| const hasInputs = Object.keys(symbolData).length > 0; | ||
|
|
||
| // Only extract inputs if there are any to avoid data loss | ||
| if (hasInputs) { |
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.
can you explain why we only extract inputs?
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.
we extract inputs from symbolData (which comes from symbol.data) to make them visible as top-level JSX props.
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.
might be good to document this in the code
Deploying mitosis with
|
| Latest commit: |
5c65b84
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://9d8136c7.mitosis-9uh.pages.dev |
| Branch Preview URL: | https://nkoech-symbols-investigation.mitosis-9uh.pages.dev |
…ol.options.data and contains key-value pairs for props passed to the symbol instance in Builder.io
Symbol AI Improvements: Named Components and Top-Level Input Props
Summary
Improves Builder Symbol serialization to Mitosis JSX to make symbols more understandable for LLMs in Visual Editor AI. Symbols now serialize with meaningful component names and inputs as top-level props instead of nested in
symbol.data.Motivation
Currently, all Builder Symbols serialize to generic
<Symbol>tags with inputs buried in nestedsymbol.dataobjects. This creates several challenges for LLMs:<Symbol>tags all look identical, making it impossible for LLMs to differentiate between a header symbol and a button symbolsymbol.datadon't follow standard JSX patterns that LLMs are trained onChanges Made
Phase 1: Symbol Name Serialization
sanitizeSymbolName()helper function that converts symbol names to valid JSX component names (e.g., "Header Navigation" → "SymbolHeaderNavigation")Symbolcomponent mapper to use sanitized names instead of generic'Symbol'extractSymbols()function to use actual symbol names when creating subComponentsextractSymbols()by checking if component name starts with "Symbol"Phase 2: Inputs as Top-Level Props
symbol.dataand created individual bindings for each inputsymbolbinding to avoid duplicationdata: {}objects to prevent data loss for symbols without inputsFiles Changed
packages/core/src/parsers/builder/builder.ts- Core implementationpackages/core/src/__tests__/builder/builder.test.ts- Integration testspackages/core/src/__tests__/data/builder/symbol-*.json- Test fixtures (4 new files)Testing
Added comprehensive test coverage:
All 10,236 tests passing.
Before & After
Before:
After:
Impact on Visual Editor AI
This change significantly improves LLM understanding of symbols:
Better targeting: LLMs can now distinguish between different symbols by name
<SymbolHeaderNavigation><SymbolButtonComponent>Natural JSX patterns: Top-level props follow standard JSX conventions that LLMs are trained on
buttonText="Click me!"is a propImproved editability: LLMs can more easily modify symbol inputs
Testing
Symbol Roundtrip Documentation
This document describes how Builder Symbols are serialized through the Mitosis JSX roundtrip process used by Editor AI.
Overview
The roundtrip flow is:
This allows the AI to see and manipulate symbols as readable JSX, while preserving all metadata when converting back to Builder format.
Roundtrip Example
Step 1: Original Builder JSON (from MCP/API)
This is what Builder stores and what the visual editor expects:
{ "@type": "@builder.io/sdk:Element", "id": "builder-abc123", "component": { "name": "Symbol", "options": { "symbol": { "entry": "2f27304b0ca04f578466218e27ae6d9b", "model": "symbol", "name": "Copyright Reserved", "data": { "buttonText": "Click me!", "year": 2025 } } } }, "responsiveStyles": { "large": { "display": "flex", "flexDirection": "column", "position": "relative", "flexShrink": "0", "boxSizing": "border-box" } } }Key properties:
component.name: Always"Symbol"- required by Builder visual editorsymbol.entry: Unique ID linking to the symbol contentsymbol.name: Human-readable display namesymbol.data: Input values for this symbol instanceStep 2: Mitosis Component (internal representation)
After
builderContentToMitosisComponent():Transformations:
SymbolCopyrightReserved(sanitized, prefixed with "Symbol")symbol.dataare extracted as top-level bindingssymbol.datais removed from the symbol binding (to avoid duplication)Step 3: Mitosis JSX String (what AI sees)
After
componentToMitosis():Benefits for AI:
SymbolCopyrightReserved) is distinguishable from other symbolsStep 4: After parseJsx() (after AI edits)
After
parseJsx():Note: Simple string values like
buttonTextbecomepropertiesinstead ofbindingsafter JSX parsing. The generator handles both.Step 5: Back to Builder JSON (for visual editor)
After
componentToBuilder():{ "@type": "@builder.io/sdk:Element", "component": { "name": "Symbol", "options": { "symbol": { "entry": "2f27304b0ca04f578466218e27ae6d9b", "model": "symbol", "name": "Copyright Reserved", "data": { "buttonText": "Click me!", "year": "2025" } } } } }Transformations:
component.nameis reset to"Symbol"(required by Builder)symbol.nameis preserved for future roundtripsbindingsandpropertiesare merged back intosymbol.dataKey Implementation Details
Name Sanitization
The
sanitizeSymbolName()function converts display names to valid JSX component names:Rules:
Input Extraction
Inputs are extracted from
symbol.dataas top-level props for AI readability:Input Merging (on way back)
The generator merges inputs from both sources:
AI Interaction Rules
The AI is instructed:
symbolprop (entry, model, name)Files Involved
parsers/builder/builder.tsgenerators/mitosis/index.tsparsers/jsx/index.tsgenerators/builder/generator.tsai-services/.../parse-content-value.ts