AgentSkillsCN

inspector-customization

为节点编辑器创建和自定义检查器面板。当实现自定义检查器选项卡、设置面板、节点特定检查器,或使用检查器 UI 组件时使用。

SKILL.md
--- frontmatter
name: inspector-customization
description: Create and customize inspector panels for node editors. Use when implementing custom inspector tabs, settings panels, node-specific inspectors, or using inspector UI components.

Inspector Customization

This skill covers creating and customizing inspector panels in react-wireflow.

Three Customization Patterns

Pattern 1: Node-Level renderInspector

Define custom inspector content per node type in the NodeDefinition:

typescript
import type { NodeDefinition, InspectorRenderProps } from "react-wireflow";
import {
  PropertySection,
  InspectorDefinitionList,
  InspectorDefinitionItem,
  InspectorInput,
  InspectorSelect,
} from "react-wireflow";

type PersonNodeData = {
  name: string;
  email: string;
  role: "developer" | "designer" | "manager";
};

function PersonInspectorRenderer({
  node,
  onUpdateNode,
}: InspectorRenderProps<PersonNodeData>): React.ReactElement {
  const data = node.data ?? ({} as PersonNodeData);

  const handleChange = <K extends keyof PersonNodeData>(
    key: K,
    value: PersonNodeData[K]
  ) => {
    onUpdateNode({ data: { ...data, [key]: value } });
  };

  return (
    <PropertySection title="Person Details">
      <InspectorDefinitionList>
        <InspectorDefinitionItem label="Name">
          <InspectorInput
            value={data.name ?? ""}
            onChange={(e) => handleChange("name", e.target.value)}
            placeholder="Enter name"
          />
        </InspectorDefinitionItem>
        <InspectorDefinitionItem label="Role">
          <InspectorSelect
            value={data.role ?? "developer"}
            onChange={(e) =>
              handleChange("role", e.target.value as PersonNodeData["role"])
            }
          >
            <option value="developer">Developer</option>
            <option value="designer">Designer</option>
            <option value="manager">Manager</option>
          </InspectorSelect>
        </InspectorDefinitionItem>
      </InspectorDefinitionList>
    </PropertySection>
  );
}

const PersonNodeDefinition: NodeDefinition<PersonNodeData> = {
  type: "person",
  displayName: "Person",
  ports: [...],
  renderInspector: PersonInspectorRenderer,
};

Pattern 2: Custom Tabs via InspectorPanel

Add custom tabs to the inspector panel:

typescript
import {
  InspectorPanel,
  InspectorLayersTab,
  InspectorPropertiesTab,
  InspectorSettingsTab,
  InspectorSection,
  PropertySection,
  type InspectorPanelTabConfig,
} from "react-wireflow";

const StatisticsTab: React.FC = () => (
  <InspectorSection>
    <PropertySection title="Editor Statistics">
      <InspectorDefinitionList>
        <InspectorDefinitionItem label="Total Nodes">
          <ReadOnlyField>5</ReadOnlyField>
        </InspectorDefinitionItem>
      </InspectorDefinitionList>
    </PropertySection>
  </InspectorSection>
);

const CustomInspectorPanel: React.FC = () => {
  const tabs: InspectorPanelTabConfig[] = React.useMemo(
    () => [
      {
        id: "layers",
        label: "Layers",
        render: () => <InspectorLayersTab />,
      },
      {
        id: "properties",
        label: "Properties",
        render: () => <InspectorPropertiesTab />,
      },
      {
        id: "statistics",
        label: "Stats",
        render: () => <StatisticsTab />,
      },
      {
        id: "settings",
        label: "Settings",
        render: () => <InspectorSettingsTab />,
      },
    ],
    []
  );

  return <InspectorPanel tabs={tabs} />;
};

Pattern 3: Custom Settings Panels

Add custom panels to the Settings tab:

typescript
import {
  InspectorSettingsTab,
  InspectorDefinitionList,
  InspectorDefinitionItem,
  InspectorSelect,
  InspectorButton,
  type InspectorSettingsPanelConfig,
} from "react-wireflow";

const ExportSettingsPanel: React.FC = () => {
  const [format, setFormat] = React.useState<"json" | "yaml">("json");

  return (
    <InspectorDefinitionList>
      <InspectorDefinitionItem label="Format">
        <InspectorSelect
          value={format}
          onChange={(e) => setFormat(e.target.value as "json" | "yaml")}
        >
          <option value="json">JSON</option>
          <option value="yaml">YAML</option>
        </InspectorSelect>
      </InspectorDefinitionItem>
      <InspectorDefinitionItem label="">
        <InspectorButton variant="primary" size="small">
          Export Data
        </InspectorButton>
      </InspectorDefinitionItem>
    </InspectorDefinitionList>
  );
};

const settingsPanels: InspectorSettingsPanelConfig[] = [
  {
    title: "Export Options",
    component: ExportSettingsPanel,
  },
];

// Use in tabs:
{
  id: "settings",
  label: "Settings",
  render: () => <InspectorSettingsTab panels={settingsPanels} />,
}

Available Inspector Components

Panel Components

ComponentDescription
InspectorPanelMain container with tab support
InspectorLayersTabBuilt-in layers/tree tab
InspectorPropertiesTabBuilt-in properties tab (uses node's renderInspector)
InspectorSettingsTabBuilt-in settings tab with extensible panels
InspectorHistoryTabBuilt-in undo/redo history tab

Layout Components

ComponentDescription
InspectorSectionBasic section wrapper
PropertySectionCollapsible section with title
InspectorFieldVertical field layout (label above input)
InspectorFieldRowHorizontal field layout
InspectorDefinitionListDefinition list container
InspectorDefinitionItemLabel-value pair in definition list
PositionInputsGridGrid for X/Y coordinate inputs
InspectorSectionTitleStandalone section title
InspectorTabbedContainerNested tabs within inspector

Input Components

ComponentDescription
InspectorInputText input field
InspectorNumberInputNumber input with label
InspectorTextareaMulti-line text input
InspectorSelectDropdown select
InspectorButtonButton (variants: primary, danger, default)
InspectorIconButtonIcon-only button
InspectorButtonGroupSegmented button group (radio-style)
InspectorToggleGroupToggle button group (checkbox-style)
InspectorLabelForm label
ReadOnlyFieldNon-editable display field

InspectorRenderProps Reference

typescript
type InspectorRenderProps<TData> = {
  node: Node & { data: TData };
  externalData: unknown;
  isLoadingExternalData: boolean;
  externalDataError: Error | null;
  onUpdateNode: (updates: Partial<Node>) => void;
  onUpdateExternalData: (data: unknown) => Promise<void>;
  onDeleteNode: () => void;
};

Best Practices

  1. Always use inspector components for consistent theming across light/dark modes
  2. Use PropertySection for collapsible groups of related fields
  3. Use InspectorDefinitionList for label-value pairs
  4. Use InspectorButtonGroup for mutually exclusive options
  5. Keep inspector content scrollable - avoid fixed heights

Example File

See complete example at: src/examples/demos/custom/inspector/custom-inspector/CustomInspectorExample.tsx