From 9c38a3f11a8616ab57130fd154876d4b8a1b28ba Mon Sep 17 00:00:00 2001 From: anti Date: Wed, 22 Apr 2026 18:19:21 -0400 Subject: [PATCH] feat(web/mazenet): group Service Fleet items by category (Remote Access, Web, Databases, etc.) --- decnet_web/src/components/MazeNET/MazeNET.css | 6 +++ decnet_web/src/components/MazeNET/Palette.tsx | 51 +++++++++++++------ decnet_web/src/components/MazeNET/data.ts | 49 ++++++++++++------ .../src/components/MazeNET/useMazeApi.ts | 1 + 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/decnet_web/src/components/MazeNET/MazeNET.css b/decnet_web/src/components/MazeNET/MazeNET.css index 46e6e2f3..c715b7bf 100644 --- a/decnet_web/src/components/MazeNET/MazeNET.css +++ b/decnet_web/src/components/MazeNET/MazeNET.css @@ -110,6 +110,12 @@ body.maze-fullscreen .maze-shell { .palette-hint { font-size: 0.62rem; opacity: 0.5; line-height: 1.6; letter-spacing: 0.5px; } +.palette-subgroup { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; } +.palette-subgroup:first-child { margin-top: 0; } +.palette-subgroup-label { + font-size: 0.55rem; letter-spacing: 1.5px; opacity: 0.4; + color: var(--violet); margin-top: 6px; +} .violet-accent { color: var(--violet); } .alert-text { color: var(--alert); } diff --git a/decnet_web/src/components/MazeNET/Palette.tsx b/decnet_web/src/components/MazeNET/Palette.tsx index 8debfbcf..1d0dd9e0 100644 --- a/decnet_web/src/components/MazeNET/Palette.tsx +++ b/decnet_web/src/components/MazeNET/Palette.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { GitMerge, ShieldAlert, Server, Monitor, Shield, Database, Cpu, Globe, Terminal, Lock, Folder, HardDrive, Users, KeyRound, Radio, Zap, Wifi, Circle } from 'lucide-react'; -import type { ServiceDef, Archetype } from './data'; +import type { ServiceDef, Archetype, ServiceGroup } from './data'; +import { SERVICE_GROUP_ORDER } from './data'; import type { PaletteDrag } from './useMazeInteraction'; const ICON: Record> = { @@ -66,21 +67,39 @@ const Palette: React.FC = ({ services, archetypes, startPaletteDrag, clas
- {services.map((s) => ( -
- - {s.name} - {s.proto.toUpperCase()}:{s.port} -
- ))} + {(() => { + const byGroup = new Map(); + for (const s of services) { + const g = (s.group ?? 'Remote Access') 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} +
+ ))} +
+ )); + })()}
diff --git a/decnet_web/src/components/MazeNET/data.ts b/decnet_web/src/components/MazeNET/data.ts index af4e8ea5..315fe361 100644 --- a/decnet_web/src/components/MazeNET/data.ts +++ b/decnet_web/src/components/MazeNET/data.ts @@ -12,8 +12,27 @@ export interface ServiceDef { proto: 'tcp' | 'udp'; icon: string; risk: 'low' | 'med' | 'high'; + group: ServiceGroup; } +export type ServiceGroup = + | 'Remote Access' + | 'Web' + | 'File Transfer' + | 'Directory' + | 'Databases' + | 'IoT / OT'; + +// Rendering order for the palette. +export const SERVICE_GROUP_ORDER: ServiceGroup[] = [ + 'Remote Access', + 'Web', + 'File Transfer', + 'Directory', + 'Databases', + 'IoT / OT', +]; + export const ARCHETYPES: Archetype[] = [ { slug: 'linux-server', name: 'Linux Server', services: ['ssh', 'http'], icon: 'server' }, { slug: 'windows-workstation', name: 'Windows Workstation', services: ['smb', 'rdp'], icon: 'monitor' }, @@ -24,20 +43,20 @@ export const ARCHETYPES: Archetype[] = [ ]; export const DEFAULT_SERVICES: ServiceDef[] = [ - { slug: 'ssh', name: 'SSH', port: 22, proto: 'tcp', icon: 'terminal', risk: 'high' }, - { slug: 'http', name: 'HTTP', port: 80, proto: 'tcp', icon: 'globe', risk: 'med' }, - { slug: 'https', name: 'HTTPS', port: 443, proto: 'tcp', icon: 'lock', risk: 'med' }, - { slug: 'ftp', name: 'FTP', port: 21, proto: 'tcp', icon: 'folder', risk: 'high' }, - { slug: 'smb', name: 'SMB', port: 445, proto: 'tcp', icon: 'hard-drive', risk: 'high' }, - { slug: 'rdp', name: 'RDP', port: 3389, proto: 'tcp', icon: 'monitor', risk: 'high' }, - { slug: 'ldap', name: 'LDAP', port: 389, proto: 'tcp', icon: 'users', risk: 'med' }, - { slug: 'kerberos', name: 'Kerberos', port: 88, proto: 'tcp', icon: 'key-round', risk: 'med' }, - { slug: 'llmnr', name: 'LLMNR', port: 5355, proto: 'udp', icon: 'radio', risk: 'low' }, - { slug: 'mysql', name: 'MySQL', port: 3306, proto: 'tcp', icon: 'database', risk: 'high' }, - { slug: 'postgres', name: 'Postgres', port: 5432, proto: 'tcp', icon: 'database', risk: 'high' }, - { slug: 'redis', name: 'Redis', port: 6379, proto: 'tcp', icon: 'zap', risk: 'med' }, - { slug: 'mqtt', name: 'MQTT', port: 1883, proto: 'tcp', icon: 'wifi', risk: 'low' }, - { slug: 'modbus', name: 'Modbus', port: 502, proto: 'tcp', icon: 'cpu', risk: 'med' }, - { slug: 'coap', name: 'CoAP', port: 5683, proto: 'udp', icon: 'wifi', risk: 'low' }, + { slug: 'ssh', name: 'SSH', port: 22, proto: 'tcp', icon: 'terminal', risk: 'high', group: 'Remote Access' }, + { slug: 'rdp', name: 'RDP', port: 3389, proto: 'tcp', icon: 'monitor', risk: 'high', group: 'Remote Access' }, + { slug: 'http', name: 'HTTP', port: 80, proto: 'tcp', icon: 'globe', risk: 'med', group: 'Web' }, + { slug: 'https', name: 'HTTPS', port: 443, proto: 'tcp', icon: 'lock', risk: 'med', group: 'Web' }, + { slug: 'ftp', name: 'FTP', port: 21, proto: 'tcp', icon: 'folder', risk: 'high', group: 'File Transfer' }, + { slug: 'smb', name: 'SMB', port: 445, proto: 'tcp', icon: 'hard-drive', risk: 'high', group: 'File Transfer' }, + { slug: 'ldap', name: 'LDAP', port: 389, proto: 'tcp', icon: 'users', risk: 'med', group: 'Directory' }, + { slug: 'kerberos', name: 'Kerberos', port: 88, proto: 'tcp', icon: 'key-round', risk: 'med', group: 'Directory' }, + { slug: 'llmnr', name: 'LLMNR', port: 5355, proto: 'udp', icon: 'radio', risk: 'low', group: 'Directory' }, + { slug: 'mysql', name: 'MySQL', port: 3306, proto: 'tcp', icon: 'database', risk: 'high', group: 'Databases' }, + { slug: 'postgres', name: 'Postgres', port: 5432, proto: 'tcp', icon: 'database', risk: 'high', group: 'Databases' }, + { slug: 'redis', name: 'Redis', port: 6379, proto: 'tcp', icon: 'zap', risk: 'med', group: 'Databases' }, + { slug: 'mqtt', name: 'MQTT', port: 1883, proto: 'tcp', icon: 'wifi', risk: 'low', group: 'IoT / OT' }, + { slug: 'modbus', name: 'Modbus', port: 502, proto: 'tcp', icon: 'cpu', risk: 'med', group: 'IoT / OT' }, + { slug: 'coap', name: 'CoAP', port: 5683, proto: 'udp', icon: 'wifi', risk: 'low', group: 'IoT / OT' }, ]; diff --git a/decnet_web/src/components/MazeNET/useMazeApi.ts b/decnet_web/src/components/MazeNET/useMazeApi.ts index 96008e0c..94a4257a 100644 --- a/decnet_web/src/components/MazeNET/useMazeApi.ts +++ b/decnet_web/src/components/MazeNET/useMazeApi.ts @@ -263,6 +263,7 @@ export function useMazeApi(): MazeApi { proto: 'tcp' as const, icon: 'circle', risk: 'low' as const, + group: 'Remote Access' as const, }, ); } catch {