import React from 'react'; import { GitMerge, ShieldAlert, Server, Monitor, Shield, Database, Cpu, Globe, Terminal, Lock, Folder, HardDrive, Users, KeyRound, Radio, Zap, Wifi, Circle, Mail, Phone, Activity, Box } from '../../icons'; import type { ServiceDef, Archetype, ServiceGroup } from './data'; import { SERVICE_GROUP_ORDER } from './data'; import type { PaletteDrag } from './useMazeInteraction'; const ICON: Record> = { 'git-merge': GitMerge, 'shield-alert': ShieldAlert, server: Server, monitor: Monitor, shield: Shield, database: Database, cpu: Cpu, globe: Globe, terminal: Terminal, lock: Lock, folder: Folder, 'hard-drive': HardDrive, users: Users, 'key-round': KeyRound, radio: Radio, zap: Zap, wifi: Wifi, circle: Circle, mail: Mail, phone: Phone, activity: Activity, box: Box, }; function Icon({ name, size = 14, className }: { name: string; size?: number; className?: string }) { const C = ICON[name] ?? Circle; return ; } interface Props { services: ServiceDef[]; archetypes: Archetype[]; startPaletteDrag: (d: Omit, e: React.MouseEvent) => void; className?: string; } const Palette: React.FC = ({ services, archetypes, startPaletteDrag, className = '' }) => { const start = (d: Omit) => (e: React.MouseEvent) => { if (e.button !== 0) return; e.preventDefault(); startPaletteDrag(d, e); }; return (
Subnet VLAN
DMZ HOST
{archetypes.map((a: Archetype) => (
{a.name} {a.services.length}
))}
{(() => { const byGroup = new Map(); for (const s of services) { const g = (s.group ?? 'Miscellaneous') as ServiceGroup; const list = byGroup.get(g) ?? []; list.push(s); byGroup.set(g, list); } const extras = [...byGroup.keys()].filter((g) => !SERVICE_GROUP_ORDER.includes(g)); const order = [...SERVICE_GROUP_ORDER, ...extras]; return order .filter((g) => byGroup.has(g)) .map((g) => (
{g.toUpperCase()}
{byGroup.get(g)!.map((s) => (
{s.name} {s.proto.toUpperCase()}/{s.port}
))}
)); })()}
Drag a network onto the canvas, or an archetype onto a network, or a service onto a decky. Right-click for menus.
); }; export default Palette;