@taazkareem/clickup-mcp-server
Version:
ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol
184 lines (183 loc) • 5.97 kB
JavaScript
/**
* SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
* SPDX-License-Identifier: MIT
*
* Color Processor Utility
*
* Processes natural language color commands and converts them to HEX color values.
* Also generates appropriate foreground (text) colors for optimal contrast.
*/
// Basic color mapping with common color names to their HEX values
const COLOR_MAP = {
// Primary colors
red: '#FF0000',
green: '#00FF00',
blue: '#0000FF',
// Secondary colors
yellow: '#FFFF00',
purple: '#800080',
orange: '#FFA500',
pink: '#FFC0CB',
brown: '#A52A2A',
// Neutrals
black: '#000000',
white: '#FFFFFF',
gray: '#808080',
grey: '#808080',
// Extended colors
navy: '#000080',
teal: '#008080',
olive: '#808000',
maroon: '#800000',
aqua: '#00FFFF',
cyan: '#00FFFF',
magenta: '#FF00FF',
fuchsia: '#FF00FF',
lime: '#00FF00',
indigo: '#4B0082',
violet: '#EE82EE',
gold: '#FFD700',
silver: '#C0C0C0',
beige: '#F5F5DC',
tan: '#D2B48C',
coral: '#FF7F50',
crimson: '#DC143C',
khaki: '#F0E68C',
lavender: '#E6E6FA',
plum: '#DDA0DD',
salmon: '#FA8072',
turquoise: '#40E0D0',
};
// Extended color variations
const COLOR_VARIATIONS = {
red: {
light: '#FF6666',
dark: '#8B0000',
bright: '#FF0000',
deep: '#8B0000',
},
blue: {
light: '#ADD8E6',
dark: '#00008B',
sky: '#87CEEB',
navy: '#000080',
royal: '#4169E1',
deep: '#00008B',
},
green: {
light: '#90EE90',
dark: '#006400',
forest: '#228B22',
lime: '#32CD32',
mint: '#98FB98',
olive: '#808000',
},
yellow: {
light: '#FFFFE0',
dark: '#BDB76B',
pale: '#FFF9C4',
gold: '#FFD700',
lemon: '#FFFACD',
},
// Add more variations for other colors as needed
};
/**
* Extracts a color name from natural language text
* @param text - Natural language text that contains a color reference
* @returns The extracted color name or null if no color is found
*/
function extractColorFromText(text) {
if (!text)
return null;
// Convert to lowercase for case-insensitive matching
const lowercaseText = text.toLowerCase();
// First check for color variations (e.g., "dark blue", "light green")
for (const [baseColor, variations] of Object.entries(COLOR_VARIATIONS)) {
for (const [variation, _] of Object.entries(variations)) {
const colorPhrase = `${variation} ${baseColor}`;
if (lowercaseText.includes(colorPhrase)) {
return colorPhrase;
}
}
}
// Then check for base colors
for (const color of Object.keys(COLOR_MAP)) {
// Use word boundary to make sure we're matching whole words
const regex = new RegExp(`\\b${color}\\b`, 'i');
if (regex.test(lowercaseText)) {
return color;
}
}
return null;
}
/**
* Converts a color name to its HEX value
* @param colorName - Name of the color to convert (e.g., "blue", "dark red")
* @returns HEX color code or null if color name is not recognized
*/
function colorNameToHex(colorName) {
if (!colorName)
return null;
const lowercaseColor = colorName.toLowerCase();
// Check if it's a color variation (e.g., "dark blue")
const parts = lowercaseColor.split(' ');
if (parts.length === 2) {
const variation = parts[0];
const baseColor = parts[1];
if (COLOR_VARIATIONS[baseColor] && COLOR_VARIATIONS[baseColor][variation]) {
return COLOR_VARIATIONS[baseColor][variation];
}
}
// Check if it's a base color
return COLOR_MAP[lowercaseColor] || null;
}
/**
* Calculates the relative luminance of a color for WCAG contrast calculations
* @param hex - HEX color code
* @returns Relative luminance value
*/
function calculateLuminance(hex) {
// Remove # if present
const color = hex.startsWith('#') ? hex.slice(1) : hex;
// Convert HEX to RGB
const r = parseInt(color.substr(0, 2), 16) / 255;
const g = parseInt(color.substr(2, 2), 16) / 255;
const b = parseInt(color.substr(4, 2), 16) / 255;
// Calculate luminance using the formula from WCAG 2.0
const R = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4);
const G = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4);
const B = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
/**
* Generates a contrasting foreground color for optimal readability
* @param backgroundColor - HEX code of the background color
* @returns HEX code of the foreground color (either black or white)
*/
function generateContrastingForeground(backgroundColor) {
const luminance = calculateLuminance(backgroundColor);
// Use white text on dark backgrounds and black text on light backgrounds
// The threshold 0.5 is based on WCAG guidelines for contrast
return luminance > 0.5 ? '#000000' : '#FFFFFF';
}
/**
* Processes a natural language command to extract a color and convert it to HEX values
* @param command - Natural language command (e.g., "Create a blue tag")
* @returns Object containing background and foreground HEX colors, or null if color not recognized
*/
export function processColorCommand(command) {
// Extract color name from command
const colorName = extractColorFromText(command);
if (!colorName)
return null;
// Convert color name to HEX background color
const backgroundColor = colorNameToHex(colorName);
if (!backgroundColor)
return null;
// Generate appropriate foreground color
const foregroundColor = generateContrastingForeground(backgroundColor);
return {
background: backgroundColor,
foreground: foregroundColor
};
}