packages/interface/src/components/Inspector/variants/README.md
The Inspector has been refactored to support multiple resource types using composition instead of conditionals.
Inspector.tsx # Generic container
├── FileInspector.tsx # File variant
├── LocationInspector.tsx # Location variant
├── DeviceInspector.tsx # Device variant (future)
├── VolumeInspector.tsx # Volume variant (future)
└── shared/ # Shared tab components
├── ActivityTab.tsx # (future)
└── ChatTab.tsx # (future)
import { Inspector, type InspectorVariant } from '@sd/interface';
function MyComponent() {
const [variant, setVariant] = useState<InspectorVariant>(null);
// When a file is selected
function handleFileSelect(file: File) {
setVariant({ type: 'file', file });
}
// When a location is viewed (no file selected)
function handleLocationView(location: LocationInfo) {
setVariant({ type: 'location', location });
}
// Clear selection
function handleClear() {
setVariant({ type: 'empty' });
}
return (
<div className="flex">
<div className="flex-1">...</div>
<div className="w-[280px]">
<Inspector
variant={variant}
onPopOut={handlePopOut}
showPopOutButton={true}
/>
</div>
</div>
);
}
type InspectorVariant =
| { type: 'file'; file: File }
| { type: 'location'; location: LocationInfo }
| { type: 'device'; device: DeviceInfo } // Future
| { type: 'volume'; volume: VolumeInfo } // Future
| { type: 'empty' }
| null;
[Resource]Inspector.tsx in inspectors/InspectorVariant union typeInspector.tsx containerinspectors/index.ts// inspectors/DeviceInspector.tsx
import { useState } from 'react';
import { Info, HardDrive } from '@phosphor-icons/react';
import type { DeviceInfo } from '@sd/ts-client';
export function DeviceInspector({ device }: { device: DeviceInfo }) {
const [activeTab, setActiveTab] = useState('overview');
const tabs = [
{ id: 'overview', label: 'Overview', icon: Info },
{ id: 'storage', label: 'Storage', icon: HardDrive },
// ... more tabs
];
return (
<>
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} />
<div className="flex-1 overflow-hidden flex flex-col mt-2.5">
<TabContent id="overview" activeTab={activeTab}>
<OverviewTab device={device} />
</TabContent>
</div>
</>
);
}
Then update Inspector.tsx:
// Inspector.tsx
import { DeviceInspector } from './inspectors/DeviceInspector';
export type InspectorVariant =
| { type: 'file'; file: File }
| { type: 'location'; location: LocationInfo }
| { type: 'device'; device: DeviceInfo } // Add this
| { type: 'empty' }
| null;
// In render:
{variant.type === 'device' ? (
<DeviceInspector device={variant.device} />
) : ...}
All inspectors use shared primitives from components/Inspector/:
<Section> - Container with title and icon<InfoRow> - Label/value pair<Tabs> - Tab navigation<TabContent> - Tab content wrapper<Divider> - Horizontal divider<Tag> - Colored tag badgeClean separation - Each variant is self-contained Type-safe - Union types ensure correct data Extensible - Add variants without touching existing code Reusable - Shared primitives reduce duplication Maintainable - No conditional rendering mess