The SDK editor's sidebar settings are currently all stubbed (settings-stub.ts returns () => null for every component). The old main-app approach had 17 separate *Settings.tsx React components — one per component type — each re-declaring the same tab structure with minor variations. This caused the entire sidebar to unmount/remount when switching between selected components, leading to flicker, lost scroll position, and wasted renders.
Goal: Build a single Inspector component that renders the sidebar shell once. Standard sections (Appearance, Layout, Hover & Click, Animations, Accessibility, Style) are always present and driven by a config object per component type. When selection changes, only the config/values change — React reconciles minimal DOM diffs instead of tearing down the whole tree.
Each component type declares a plain config object describing what its settings look like:
interface ComponentSettingsSchema {
displayName: string; // Tab label ("Text", "Button", etc.)
icon: ReactNode; // Tab icon
MainTab: ComponentType; // Component-specific content (1st tab)
// Standard section configs (each is data, not JSX)
appearance?: AppearanceConfig; // { textColor, bgColor, font, border, shadow, ... }
layout?: "spacing" | "hidden" | ReactNode;
hoverClick?: { variant: string } | "hidden" | ReactNode;
animations?: "standard" | "hidden";
accessibility?: "standard" | "hidden";
style?: StyleConfig; // { displaySettings, typeInput, importExport }
}
Standard sections render from these config flags — no custom JSX needed for 90% of cases. The first tab (MainTab) is always a React component because component-specific UIs (codemirror editors, child accordions, media uploaders) can't be expressed as data.
Selection changes
→ Inspector reads node.data.displayName
→ Looks up schema from registry
→ buildHead(schema) → tab icons (memoized, changes rarely)
→ buildSections(schema) → section config passed to static renderers
→ Standard section components receive new config props, reconcile in-place
→ MainTab component swaps (only part that remounts)
packages/sdk/src/chrome/toolbar/Inspector/
├── index.ts # barrel export
├── types.ts # ComponentSettingsSchema + sub-types
├── Inspector.tsx # the single settings component
├── registry.ts # schema definitions + lookup map
├── builders.ts # buildHead() + buildSections()
├── sections/
│ ├── AppearanceSection.tsx # renders from AppearanceConfig
│ ├── LayoutSection.tsx # renders SpacingInput or custom
│ ├── HoverClickSection.tsx # renders HoverClickInput w/ variant
│ ├── AnimationsSection.tsx # renders AnimationsInput or Notice
│ ├── AccessibilitySection.tsx # renders AccessibilityInput
│ └── StyleSection.tsx # renders DisplaySettings + TypeInput + extras
└── mainTabs/
├── TextMainTab.tsx # codemirror, presets, AI, ipsum
├── ButtonMainTab.tsx # text input, presets, icon, type
├── ContainerMainTab.tsx # page/imageContainer branching, presets, order
├── ImageMainTab.tsx # media upload, presets, size, loading
├── VideoMainTab.tsx # provider, videoId
├── AudioMainTab.tsx # audio source, playback
├── EmbedMainTab.tsx # embed code editor
├── FormMainTab.tsx # form type, submission config
├── FormElementMainTab.tsx # input type, options, validation
├── ButtonListMainTab.tsx # child accordion w/ NodeProvider
├── ImageListMainTab.tsx # child management, infinite scroll
├── DividerMainTab.tsx # style, thickness
├── SpacerMainTab.tsx # height control
└── BackgroundMainTab.tsx # background image settings
| File | Change |
|---|---|
Each packages/sdk/src/components/*.tsx (17 files) | Change .craft.related.toolbar → Inspector |
packages/sdk/src/components/settings-stub.ts | Delete after migration |
chrome/toolbar/Inputs/* — all 60+ Input components stay as-is (they use useNode() internally)chrome/toolbar/ToolbarItem.tsx — base input componentchrome/toolbar/ToolbarSection.tsx — collapsible section wrapperchrome/toolbar/InspectorTab.tsx — tab header + scrollable section bodychrome/toolbar/primitives/tableBodyControls.tsx — TBWrap wrapper (formerly Helpers/SettingsHelper.tsx)chrome/toolbar/ToolBarWrapper.tsx — footer with delete/clone/copy/pasteutils/selectorPresets.ts — centralized preset definitionschrome/toolbar/index.tsx — React.createElement(related.toolbar) dispatch (unchanged)Build infrastructure first, then migrate components simplest-to-hardest:
settings-stub.ts, move orphaned atom exportsEach phase is independently shippable — old stubs and new unified system coexist since each component independently references its toolbar.
npm run dev and verify no console errorscd packages/sdk && npm run build) to confirm bundling works