Initial public release of BreznFlow, an n8n workflow renderer for WordPress. Fully PHPCS-compliant (WordPress Coding Standards), security-hardened, and ready for WordPress.org plugin review. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
813 lines
20 KiB
PHP
813 lines
20 KiB
PHP
<?php
|
|
/**
|
|
* Registry of known n8n node types with display metadata.
|
|
*
|
|
* @package BreznFlow
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
namespace BreznFlow\Features;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Registry of known n8n node types with their display metadata.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class NodeTypeRegistry {
|
|
/**
|
|
* Returns the full node type registry.
|
|
* Keys are n8n node slugs (part after last dot in type string).
|
|
*
|
|
* @return array<string, array{label: string, icon: string, symbol?: string, color: string, bg: string}>
|
|
*/
|
|
public static function get_registry(): array {
|
|
return array(
|
|
// Triggers.
|
|
'scheduleTrigger' => array(
|
|
'label' => 'Schedule Trigger',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⏰',
|
|
'color' => '#31C48D',
|
|
'bg' => '#1a3a2e',
|
|
),
|
|
'webhook' => array(
|
|
'label' => 'Webhook',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⇆',
|
|
'color' => '#5277D4',
|
|
'bg' => '#1a2040',
|
|
),
|
|
'manualTrigger' => array(
|
|
'label' => 'Manual Trigger',
|
|
'icon' => 'symbol',
|
|
'symbol' => '▶',
|
|
'color' => '#FF6B35',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'formTrigger' => array(
|
|
'label' => 'Form Trigger',
|
|
'icon' => 'symbol',
|
|
'symbol' => '▤',
|
|
'color' => '#9B59B6',
|
|
'bg' => '#2a1040',
|
|
),
|
|
'emailReadImap' => array(
|
|
'label' => 'Email (IMAP)',
|
|
'icon' => 'symbol',
|
|
'symbol' => '✉',
|
|
'color' => '#E74C3C',
|
|
'bg' => '#3a1010',
|
|
),
|
|
'respondToWebhook' => array(
|
|
'label' => 'Respond to Webhook',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↩',
|
|
'color' => '#5277D4',
|
|
'bg' => '#1a2040',
|
|
),
|
|
|
|
// Core logic.
|
|
'code' => array(
|
|
'label' => 'Code',
|
|
'icon' => 'symbol',
|
|
'symbol' => '{ }',
|
|
'color' => '#FF9500',
|
|
'bg' => '#3a2000',
|
|
),
|
|
'function' => array(
|
|
'label' => 'Function',
|
|
'icon' => 'symbol',
|
|
'symbol' => 'ƒ',
|
|
'color' => '#FF9500',
|
|
'bg' => '#3a2000',
|
|
),
|
|
'executeWorkflow' => array(
|
|
'label' => 'Execute Workflow',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↺',
|
|
'color' => '#7F8C8D',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'httpRequest' => array(
|
|
'label' => 'HTTP Request',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⊕',
|
|
'color' => '#5277D4',
|
|
'bg' => '#1a2040',
|
|
),
|
|
'wait' => array(
|
|
'label' => 'Wait',
|
|
'icon' => 'symbol',
|
|
'symbol' => 'II',
|
|
'color' => '#9B59B6',
|
|
'bg' => '#2a1040',
|
|
),
|
|
'if' => array(
|
|
'label' => 'If',
|
|
'icon' => 'symbol',
|
|
'symbol' => '?',
|
|
'color' => '#F39C12',
|
|
'bg' => '#3a2800',
|
|
),
|
|
'switch' => array(
|
|
'label' => 'Switch',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⋈',
|
|
'color' => '#F39C12',
|
|
'bg' => '#3a2800',
|
|
),
|
|
'merge' => array(
|
|
'label' => 'Merge',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⊕',
|
|
'color' => '#26C6DA',
|
|
'bg' => '#003040',
|
|
),
|
|
'splitInBatches' => array(
|
|
'label' => 'Split in Batches',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⋮',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'set' => array(
|
|
'label' => 'Set',
|
|
'icon' => 'symbol',
|
|
'symbol' => '=',
|
|
'color' => '#26C6DA',
|
|
'bg' => '#003040',
|
|
),
|
|
'editFields' => array(
|
|
'label' => 'Edit Fields',
|
|
'icon' => 'symbol',
|
|
'symbol' => '=',
|
|
'color' => '#26C6DA',
|
|
'bg' => '#003040',
|
|
),
|
|
'filter' => array(
|
|
'label' => 'Filter',
|
|
'icon' => 'symbol',
|
|
'symbol' => '▽',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'aggregate' => array(
|
|
'label' => 'Aggregate',
|
|
'icon' => 'symbol',
|
|
'symbol' => '∑',
|
|
'color' => '#26C6DA',
|
|
'bg' => '#003040',
|
|
),
|
|
'splitOut' => array(
|
|
'label' => 'Split Out',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↔',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'sort' => array(
|
|
'label' => 'Sort',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↕',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'limit' => array(
|
|
'label' => 'Limit',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⊐',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'removeDuplicates' => array(
|
|
'label' => 'Remove Duplicates',
|
|
'icon' => 'symbol',
|
|
'symbol' => '≠',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'itemLists' => array(
|
|
'label' => 'Item Lists',
|
|
'icon' => 'symbol',
|
|
'symbol' => '≡',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'compareDatasets' => array(
|
|
'label' => 'Compare Datasets',
|
|
'icon' => 'symbol',
|
|
'symbol' => '△',
|
|
'color' => '#26C6DA',
|
|
'bg' => '#003040',
|
|
),
|
|
'noOp' => array(
|
|
'label' => 'No Operation',
|
|
'icon' => 'symbol',
|
|
'symbol' => '∅',
|
|
'color' => '#BDC3C7',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'stickyNote' => array(
|
|
'label' => 'Sticky Note',
|
|
'icon' => 'symbol',
|
|
'symbol' => '📌',
|
|
'color' => '#F1C40F',
|
|
'bg' => '#3a3000',
|
|
),
|
|
'stopAndError' => array(
|
|
'label' => 'Stop and Error',
|
|
'icon' => 'symbol',
|
|
'symbol' => '✕',
|
|
'color' => '#E74C3C',
|
|
'bg' => '#3a1010',
|
|
),
|
|
|
|
// Data transformation.
|
|
'toFile' => array(
|
|
'label' => 'To File',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↓',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'convertToFile' => array(
|
|
'label' => 'Convert to File',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↓',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'html' => array(
|
|
'label' => 'HTML',
|
|
'icon' => 'symbol',
|
|
'symbol' => '</>',
|
|
'color' => '#E34F26',
|
|
'bg' => '#3a1000',
|
|
),
|
|
'xml' => array(
|
|
'label' => 'XML',
|
|
'icon' => 'symbol',
|
|
'symbol' => '</>',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'markdown' => array(
|
|
'label' => 'Markdown',
|
|
'icon' => 'symbol',
|
|
'symbol' => 'M↓',
|
|
'color' => '#083FA1',
|
|
'bg' => '#001040',
|
|
),
|
|
'crypto' => array(
|
|
'label' => 'Crypto',
|
|
'icon' => 'symbol',
|
|
'symbol' => '🔒',
|
|
'color' => '#F39C12',
|
|
'bg' => '#3a2800',
|
|
),
|
|
'compression' => array(
|
|
'label' => 'Compression',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⇩',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
'extractFromFile' => array(
|
|
'label' => 'Extract from File',
|
|
'icon' => 'symbol',
|
|
'symbol' => '↑',
|
|
'color' => '#95A5A6',
|
|
'bg' => '#2d2d2d',
|
|
),
|
|
|
|
// Databases.
|
|
'mySql' => array(
|
|
'label' => 'MySQL',
|
|
'icon' => 'initials',
|
|
'symbol' => 'My',
|
|
'color' => '#4479A1',
|
|
'bg' => '#001530',
|
|
),
|
|
'postgres' => array(
|
|
'label' => 'PostgreSQL',
|
|
'icon' => 'initials',
|
|
'symbol' => 'PG',
|
|
'color' => '#336791',
|
|
'bg' => '#001030',
|
|
),
|
|
'redis' => array(
|
|
'label' => 'Redis',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Re',
|
|
'color' => '#DC382D',
|
|
'bg' => '#3a1010',
|
|
),
|
|
'mongodb' => array(
|
|
'label' => 'MongoDB',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Mg',
|
|
'color' => '#4DB33D',
|
|
'bg' => '#103010',
|
|
),
|
|
'sqlite' => array(
|
|
'label' => 'SQLite',
|
|
'icon' => 'initials',
|
|
'symbol' => 'SL',
|
|
'color' => '#003B57',
|
|
'bg' => '#001020',
|
|
),
|
|
'microsoftSql' => array(
|
|
'label' => 'MS SQL',
|
|
'icon' => 'initials',
|
|
'symbol' => 'MS',
|
|
'color' => '#CC2927',
|
|
'bg' => '#3a1010',
|
|
),
|
|
'supabase' => array(
|
|
'label' => 'Supabase',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Sb',
|
|
'color' => '#3ECF8E',
|
|
'bg' => '#103020',
|
|
),
|
|
|
|
// Communication.
|
|
'slack' => array(
|
|
'label' => 'Slack',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Sl',
|
|
'color' => '#4A154B',
|
|
'bg' => '#200020',
|
|
),
|
|
'telegram' => array(
|
|
'label' => 'Telegram',
|
|
'icon' => 'symbol',
|
|
'symbol' => '✈',
|
|
'color' => '#2CA5E0',
|
|
'bg' => '#003040',
|
|
),
|
|
'discord' => array(
|
|
'label' => 'Discord',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Dc',
|
|
'color' => '#5865F2',
|
|
'bg' => '#101080',
|
|
),
|
|
'mattermost' => array(
|
|
'label' => 'Mattermost',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Mm',
|
|
'color' => '#0058CC',
|
|
'bg' => '#001840',
|
|
),
|
|
'emailSend' => array(
|
|
'label' => 'Send Email',
|
|
'icon' => 'symbol',
|
|
'symbol' => '✉',
|
|
'color' => '#E74C3C',
|
|
'bg' => '#3a1010',
|
|
),
|
|
'sendEmail' => array(
|
|
'label' => 'Send Email',
|
|
'icon' => 'symbol',
|
|
'symbol' => '✉',
|
|
'color' => '#E74C3C',
|
|
'bg' => '#3a1010',
|
|
),
|
|
'gmail' => array(
|
|
'label' => 'Gmail',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Gm',
|
|
'color' => '#EA4335',
|
|
'bg' => '#3a1010',
|
|
),
|
|
'microsoftOutlook' => array(
|
|
'label' => 'Outlook',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Ol',
|
|
'color' => '#0078D4',
|
|
'bg' => '#001840',
|
|
),
|
|
'whatsapp' => array(
|
|
'label' => 'WhatsApp',
|
|
'icon' => 'initials',
|
|
'symbol' => 'WA',
|
|
'color' => '#25D366',
|
|
'bg' => '#103020',
|
|
),
|
|
'twilio' => array(
|
|
'label' => 'Twilio',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Tw',
|
|
'color' => '#F22F46',
|
|
'bg' => '#3a1010',
|
|
),
|
|
|
|
// Google.
|
|
'googleSheets' => array(
|
|
'label' => 'Google Sheets',
|
|
'icon' => 'initials',
|
|
'symbol' => 'GS',
|
|
'color' => '#34A853',
|
|
'bg' => '#103020',
|
|
),
|
|
'googleDrive' => array(
|
|
'label' => 'Google Drive',
|
|
'icon' => 'initials',
|
|
'symbol' => 'GD',
|
|
'color' => '#4285F4',
|
|
'bg' => '#001840',
|
|
),
|
|
'googleCalendar' => array(
|
|
'label' => 'Google Calendar',
|
|
'icon' => 'initials',
|
|
'symbol' => 'GC',
|
|
'color' => '#4285F4',
|
|
'bg' => '#001840',
|
|
),
|
|
'googleDocs' => array(
|
|
'label' => 'Google Docs',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Gd',
|
|
'color' => '#4285F4',
|
|
'bg' => '#001840',
|
|
),
|
|
'youTube' => array(
|
|
'label' => 'YouTube',
|
|
'icon' => 'initials',
|
|
'symbol' => 'YT',
|
|
'color' => '#FF0000',
|
|
'bg' => '#3a0000',
|
|
),
|
|
|
|
// Dev / Version control.
|
|
'github' => array(
|
|
'label' => 'GitHub',
|
|
'icon' => 'initials',
|
|
'symbol' => 'GH',
|
|
'color' => '#24292E',
|
|
'bg' => '#000000',
|
|
),
|
|
'gitlab' => array(
|
|
'label' => 'GitLab',
|
|
'icon' => 'initials',
|
|
'symbol' => 'GL',
|
|
'color' => '#FC6D26',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'jira' => array(
|
|
'label' => 'Jira',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Ji',
|
|
'color' => '#0052CC',
|
|
'bg' => '#001840',
|
|
),
|
|
'confluence' => array(
|
|
'label' => 'Confluence',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Cf',
|
|
'color' => '#0052CC',
|
|
'bg' => '#001840',
|
|
),
|
|
'linear' => array(
|
|
'label' => 'Linear',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Li',
|
|
'color' => '#5E6AD2',
|
|
'bg' => '#101040',
|
|
),
|
|
'notion' => array(
|
|
'label' => 'Notion',
|
|
'icon' => 'initials',
|
|
'symbol' => 'No',
|
|
'color' => '#FFFFFF',
|
|
'bg' => '#1a1a1a',
|
|
),
|
|
|
|
// AI.
|
|
'openAi' => array(
|
|
'label' => 'OpenAI',
|
|
'icon' => 'initials',
|
|
'symbol' => 'OA',
|
|
'color' => '#00A67E',
|
|
'bg' => '#003020',
|
|
),
|
|
'anthropicClaude' => array(
|
|
'label' => 'Anthropic',
|
|
'icon' => 'initials',
|
|
'symbol' => 'An',
|
|
'color' => '#D97757',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'anthropic' => array(
|
|
'label' => 'Anthropic',
|
|
'icon' => 'initials',
|
|
'symbol' => 'An',
|
|
'color' => '#D97757',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'gemini' => array(
|
|
'label' => 'Gemini',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Gm',
|
|
'color' => '#4285F4',
|
|
'bg' => '#001840',
|
|
),
|
|
'googleAi' => array(
|
|
'label' => 'Google AI',
|
|
'icon' => 'initials',
|
|
'symbol' => 'GA',
|
|
'color' => '#4285F4',
|
|
'bg' => '#001840',
|
|
),
|
|
'huggingFace' => array(
|
|
'label' => 'HuggingFace',
|
|
'icon' => 'initials',
|
|
'symbol' => 'HF',
|
|
'color' => '#FFD21E',
|
|
'bg' => '#3a2a00',
|
|
),
|
|
'mistral' => array(
|
|
'label' => 'Mistral',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Mi',
|
|
'color' => '#FF7000',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'ollama' => array(
|
|
'label' => 'Ollama',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Ol',
|
|
'color' => '#7C3AED',
|
|
'bg' => '#200040',
|
|
),
|
|
'lmChatOllama' => array(
|
|
'label' => 'Ollama Chat',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Ol',
|
|
'color' => '#7C3AED',
|
|
'bg' => '#200040',
|
|
),
|
|
'cohere' => array(
|
|
'label' => 'Cohere',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Co',
|
|
'color' => '#39594D',
|
|
'bg' => '#102020',
|
|
),
|
|
'lmChatOpenAi' => array(
|
|
'label' => 'OpenAI Chat',
|
|
'icon' => 'initials',
|
|
'symbol' => 'OA',
|
|
'color' => '#00A67E',
|
|
'bg' => '#003020',
|
|
),
|
|
'lmChatAnthropic' => array(
|
|
'label' => 'Anthropic Chat',
|
|
'icon' => 'initials',
|
|
'symbol' => 'An',
|
|
'color' => '#D97757',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'agent' => array(
|
|
'label' => 'AI Agent',
|
|
'icon' => 'symbol',
|
|
'symbol' => '🤖',
|
|
'color' => '#7C3AED',
|
|
'bg' => '#200040',
|
|
),
|
|
'chainLlm' => array(
|
|
'label' => 'LLM Chain',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⛓',
|
|
'color' => '#7C3AED',
|
|
'bg' => '#200040',
|
|
),
|
|
'vectorStore' => array(
|
|
'label' => 'Vector Store',
|
|
'icon' => 'symbol',
|
|
'symbol' => '⊞',
|
|
'color' => '#7C3AED',
|
|
'bg' => '#200040',
|
|
),
|
|
'memoryBufferWindow' => array(
|
|
'label' => 'Memory Buffer',
|
|
'icon' => 'symbol',
|
|
'symbol' => '◫',
|
|
'color' => '#7C3AED',
|
|
'bg' => '#200040',
|
|
),
|
|
|
|
// Storage / Files.
|
|
'ftp' => array(
|
|
'label' => 'FTP',
|
|
'icon' => 'initials',
|
|
'symbol' => 'FT',
|
|
'color' => '#F39C12',
|
|
'bg' => '#3a2800',
|
|
),
|
|
'ssh' => array(
|
|
'label' => 'SSH',
|
|
'icon' => 'symbol',
|
|
'symbol' => '$_',
|
|
'color' => '#2ECC71',
|
|
'bg' => '#103020',
|
|
),
|
|
'readWriteFile' => array(
|
|
'label' => 'Read/Write Files',
|
|
'icon' => 'symbol',
|
|
'symbol' => '📁',
|
|
'color' => '#F39C12',
|
|
'bg' => '#3a2800',
|
|
),
|
|
'airtable' => array(
|
|
'label' => 'Airtable',
|
|
'icon' => 'initials',
|
|
'symbol' => 'At',
|
|
'color' => '#18BFFF',
|
|
'bg' => '#003040',
|
|
),
|
|
'baserow' => array(
|
|
'label' => 'Baserow',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Br',
|
|
'color' => '#4A90D9',
|
|
'bg' => '#001840',
|
|
),
|
|
|
|
// CRM / Marketing.
|
|
'hubspot' => array(
|
|
'label' => 'HubSpot',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Hs',
|
|
'color' => '#FF7A59',
|
|
'bg' => '#3a1a00',
|
|
),
|
|
'salesforce' => array(
|
|
'label' => 'Salesforce',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Sf',
|
|
'color' => '#00A1E0',
|
|
'bg' => '#003040',
|
|
),
|
|
'mailchimp' => array(
|
|
'label' => 'Mailchimp',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Mc',
|
|
'color' => '#FFE01B',
|
|
'bg' => '#3a3000',
|
|
),
|
|
'brevo' => array(
|
|
'label' => 'Brevo',
|
|
'icon' => 'initials',
|
|
'symbol' => 'Bv',
|
|
'color' => '#0092FF',
|
|
'bg' => '#003040',
|
|
),
|
|
|
|
// WordPress / CMS.
|
|
'wordpress' => array(
|
|
'label' => 'WordPress',
|
|
'icon' => 'initials',
|
|
'symbol' => 'WP',
|
|
'color' => '#21759B',
|
|
'bg' => '#002030',
|
|
),
|
|
'rssFeedRead' => array(
|
|
'label' => 'RSS Feed',
|
|
'icon' => 'symbol',
|
|
'symbol' => '◉',
|
|
'color' => '#F26522',
|
|
'bg' => '#3a1000',
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Looks up a node by its full n8n type string.
|
|
* Extracts the slug (last segment after dot) for registry lookup.
|
|
*
|
|
* @param string $type Full n8n type string e.g. "n8n-nodes-base.httpRequest".
|
|
* @return array Node type data or fallback entry.
|
|
*/
|
|
public static function lookup( string $type ): array {
|
|
$registry = self::get_registry();
|
|
$slug = self::extract_slug( $type );
|
|
|
|
// Case-sensitive exact match first.
|
|
if ( isset( $registry[ $slug ] ) ) {
|
|
return $registry[ $slug ];
|
|
}
|
|
|
|
// Case-insensitive fallback.
|
|
$slug_lower = strtolower( $slug );
|
|
foreach ( $registry as $key => $entry ) {
|
|
if ( strtolower( $key ) === $slug_lower ) {
|
|
return $entry;
|
|
}
|
|
}
|
|
|
|
// Generate fallback entry.
|
|
return self::generate_fallback( $type, $slug );
|
|
}
|
|
|
|
/**
|
|
* Extracts the node slug from a full n8n type string.
|
|
*
|
|
* @param string $type e.g. "n8n-nodes-base.httpRequest" → "httpRequest".
|
|
*/
|
|
public static function extract_slug( string $type ): string {
|
|
$parts = explode( '.', $type );
|
|
return end( $parts );
|
|
}
|
|
|
|
/**
|
|
* Generates a deterministic fallback entry for unknown node types.
|
|
* Uses djb2 hash for a consistent color.
|
|
*
|
|
* @since 1.0.0
|
|
* @param string $type Full n8n node type string.
|
|
* @param string $slug Extracted slug portion.
|
|
* @return array Fallback display metadata.
|
|
*/
|
|
private static function generate_fallback( string $type, string $slug ): array {
|
|
$hue = self::djb2_color_hue( $type );
|
|
$hex = self::hsl_to_hex( $hue, 60, 55 );
|
|
$bg = self::hsl_to_hex( $hue, 40, 15 );
|
|
|
|
// Derive 2-letter initials from slug.
|
|
$initials = self::derive_initials( $slug );
|
|
|
|
return array(
|
|
'label' => $slug,
|
|
'icon' => 'initials',
|
|
'symbol' => $initials,
|
|
'color' => $hex,
|
|
'bg' => $bg,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Derives 2-letter initials from a camelCase or PascalCase slug.
|
|
*
|
|
* @since 1.0.0
|
|
* @param string $slug Node type slug.
|
|
* @return string Two uppercase initials.
|
|
*/
|
|
private static function derive_initials( string $slug ): string {
|
|
// Split on camelCase boundaries.
|
|
$parts = preg_split( '/(?=[A-Z])/', $slug, -1, PREG_SPLIT_NO_EMPTY );
|
|
if ( ! $parts ) {
|
|
return strtoupper( substr( $slug, 0, 2 ) );
|
|
}
|
|
|
|
if ( count( $parts ) >= 2 ) {
|
|
return strtoupper( substr( $parts[0], 0, 1 ) . substr( $parts[1], 0, 1 ) );
|
|
}
|
|
return strtoupper( substr( $slug, 0, 2 ) );
|
|
}
|
|
|
|
/**
|
|
* Computes a djb2 hash and maps it to a color hue in the 0-359 range.
|
|
*
|
|
* @since 1.0.0
|
|
* @param string $str Input string to hash.
|
|
* @return int Hue value between 0 and 359.
|
|
*/
|
|
private static function djb2_color_hue( string $str ): int {
|
|
$hash = 5381;
|
|
$len = strlen( $str );
|
|
for ( $i = 0; $i < $len; $i++ ) {
|
|
$hash = ( ( $hash << 5 ) + $hash ) + ord( $str[ $i ] );
|
|
$hash &= 0xFFFFFFFF;
|
|
}
|
|
return abs( $hash ) % 360;
|
|
}
|
|
|
|
/**
|
|
* Converts HSL to 6-digit hex color string.
|
|
*
|
|
* @since 1.0.0
|
|
* @param int $h Hue (0-359).
|
|
* @param int $s Saturation (0-100).
|
|
* @param int $l Lightness (0-100).
|
|
* @return string Hex color string (e.g. "#ff8800").
|
|
*/
|
|
private static function hsl_to_hex( int $h, int $s, int $l ): string {
|
|
$s /= 100;
|
|
$l /= 100;
|
|
$a = $s * min( $l, 1 - $l );
|
|
$f = static function ( $n ) use ( $h, $l, $a ) {
|
|
$k = fmod( $n + $h / 30, 12 );
|
|
return $l - $a * max( min( $k - 3, 9 - $k, 1 ), -1 );
|
|
};
|
|
$r = (int) round( $f( 0 ) * 255 );
|
|
$g = (int) round( $f( 8 ) * 255 );
|
|
$b = (int) round( $f( 4 ) * 255 );
|
|
return sprintf( '#%02x%02x%02x', max( 0, min( 255, $r ) ), max( 0, min( 255, $g ) ), max( 0, min( 255, $b ) ) );
|
|
}
|
|
}
|