diff --git a/README.md b/README.md
index 5c372df..c34627e 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,7 @@ The central hub with 45+ algorithms across 5 categories:
| **Barcode / QR Code Generator** | Create QR codes and 1D barcodes (EAN-13, EAN-8, Code 128, Code 39) with preview and download |
| **Data Generator** | Generate mock data with templates using Faker library (UUID, ULID, Random String, Lorem Ipsum, User Profiles, API responses, SQL inserts, and more) |
| **Code Formatter** | Format and minify JSON, XML, HTML, SQL, CSS, and JavaScript with advanced filtering support (jq for JSON, XPath for XML, CSS selectors for HTML) |
+| **Color Converter** | Pick colors with eyedropper and generate code snippets for 11+ programming languages (CSS, Swift, .NET, Java, Android, Obj-C, Flutter, Unity, React Native, OpenGL, SVG) |
| **RegExp Tester** | Test regular expressions with real-time matching |
| **Unix Time Converter** | Convert between Unix timestamps and human-readable dates |
| **String Utilities** | Sort/Dedupe lines, Case conversion (camelCase, snake_case, etc.), String Inspector |
diff --git a/TOOL_STATUS.md b/TOOL_STATUS.md
index 32df695..95c2764 100644
--- a/TOOL_STATUS.md
+++ b/TOOL_STATUS.md
@@ -21,6 +21,7 @@ This document tracks the refactoring and development status of each tool compone
| BarcodeGenerator | 🟢 Done | Multi-standard barcode generator (QR, EAN-13, EAN-8, Code128, Code39). Features: configurable size, error correction levels for QR, client-side validation, download button. | Completed 2026-01-31 |
| **DataGenerator** | 🟢 Done | Template-based mock data generator with Faker integration. Features: 10 built-in presets (UUID, ULID, Random String, Lorem Ipsum, User Profile, E-commerce Product, API Response, SQL Insert, Log Entries, Credit Card), batch generation (10-1000 records), multiple output formats (JSON, XML, CSV, YAML), comprehensive help documentation with 4 tabs (Quick Start, Syntax, Faker Reference, Examples). Backend: Go templates + gofakeit library with 80+ faker functions. Replaces: RandomStringGenerator, UuidGenerator, LoremIpsumGenerator | Completed 2026-01-31 |
| **CodeFormatter** | 🟢 Done | Unified code formatting tool supporting JSON (with jq filters), XML (with XPath), HTML (with CSS selectors), SQL, CSS, and JavaScript. Features: format/minify modes, filter/query support for structured data, auto-format on input change, persistent state. Backend: Go with gojq library for jq support. Replaces: JsonFormatter, SqlFormatter | Completed 2026-01-31 |
+| **ColorConverter** | 🟢 Done | Comprehensive color conversion tool with visual picker and eyedropper support. Features: 11 programming languages (CSS, Swift, .NET, Java, Android, Obj-C, Flutter, Unity, React Native, OpenGL, SVG), 5 color formats (HEX, RGB, HSL, HSV, CMYK), color history with 10 recent colors, random color generator, copy-to-clipboard for all code snippets. Uses Carbon Tabs for language selection. | Completed 2026-02-01 |
| **CronJobParser** | 🟢 Done | Refactored to follow Carbon Design System. Features: Split-pane layout, 8 common examples in clickable tiles, real-time parsing, large centered output display, layout toggle. | Completed 2026-01-31 |
| **RegExpTester** | 🟡 In Progress | Refactored with improved UI. Features: Flag toggle tags (g, i, m, s, u, y), split-pane layout, match count in output label, error display with styling, layout toggle. | Updated 2026-01-31 |
| **TextDiffChecker** | 🟡 In Progress | Refactored with enhanced features. Features: Diff mode switcher (Lines/Words/Chars), auto-compare on input change, Clear button, improved diff view with color coding, layout toggle. | Updated 2026-01-31 |
diff --git a/src/App.jsx b/src/App.jsx
index b1b1a65..69d6174 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -16,6 +16,7 @@ import StringUtilities from './pages/StringUtilities';
import BarcodeGenerator from './pages/BarcodeGenerator';
import DataGenerator from './pages/DataGenerator';
import CodeFormatter from './pages/CodeFormatter';
+import ColorConverter from './pages/ColorConverter';
// Error boundary for catching React rendering errors
class ErrorBoundary extends React.Component {
@@ -105,6 +106,7 @@ function App() {
case 'barcode': return ;
case 'data-generator': return ;
case 'code-formatter': return ;
+ case 'color-converter': return ;
case 'regexp': return ;
case 'cron': return ;
case 'diff': return ;
diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx
index fc148e6..d0318e0 100644
--- a/src/components/Sidebar.jsx
+++ b/src/components/Sidebar.jsx
@@ -22,6 +22,7 @@ export function Sidebar({ activeTool, setActiveTool, isVisible, toggleSidebar })
{ id: 'barcode', name: 'Barcode / QR Code', icon: '▣' },
{ id: 'data-generator', name: 'Data Generator', icon: '📊' },
{ id: 'code-formatter', name: 'Code Formatter', icon: '📝' },
+ { id: 'color-converter', name: 'Color Converter', icon: '🎨' },
{ id: 'cron', name: 'Cron Job Parser', icon: '⏳' },
{ id: 'regexp', name: 'RegExp Tester', icon: '.*' },
{ id: 'diff', name: 'Text Diff Checker', icon: '⚖️' },
diff --git a/src/pages/ColorConverter.jsx b/src/pages/ColorConverter.jsx
new file mode 100644
index 0000000..1191c43
--- /dev/null
+++ b/src/pages/ColorConverter.jsx
@@ -0,0 +1,882 @@
+import React, { useState, useEffect, useCallback, useReducer, useMemo, useRef } from 'react';
+import { Button, TextInput, Tile, Tabs, TabList, Tab, TabPanels, TabPanel } from '@carbon/react';
+import { Eyedropper, Copy, ColorPalette, TrashCan } from '@carbon/icons-react';
+import { ToolHeader, ToolControls, ToolLayoutToggle } from '../components/ToolUI';
+import useLayoutToggle from '../hooks/useLayoutToggle';
+
+// Color utility functions
+const hexToRgb = (hex) => {
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex) ||
+ /^#?([a-f\d])([a-f\d])([a-f\d])$/i.exec(hex);
+ if (!result) return null;
+
+ const r = parseInt(result[1].length === 1 ? result[1] + result[1] : result[1], 16);
+ const g = parseInt(result[2].length === 1 ? result[2] + result[2] : result[2], 16);
+ const b = parseInt(result[3].length === 1 ? result[3] + result[3] : result[3], 16);
+ const a = result[4] ? parseInt(result[4].length === 1 ? result[4] + result[4] : result[4], 16) / 255 : 1;
+
+ return { r, g, b, a };
+};
+
+const rgbToHex = (r, g, b, a = 1) => {
+ const toHex = (n) => Math.round(n).toString(16).padStart(2, '0');
+ const hex = `#${toHex(r)}${toHex(g)}${toHex(b)}`;
+ if (a < 1) {
+ return `${hex}${toHex(a * 255)}`;
+ }
+ return hex;
+};
+
+const rgbToHsl = (r, g, b) => {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ let h, s, l = (max + min) / 2;
+
+ if (max === min) {
+ h = s = 0;
+ } else {
+ const d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch (max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+
+ return {
+ h: Math.round(h * 360),
+ s: Math.round(s * 100),
+ l: Math.round(l * 100)
+ };
+};
+
+const rgbToHsv = (r, g, b) => {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ let h, s, v = max;
+
+ const d = max - min;
+ s = max === 0 ? 0 : d / max;
+
+ if (max === min) {
+ h = 0;
+ } else {
+ switch (max) {
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+ case g: h = (b - r) / d + 2; break;
+ case b: h = (r - g) / d + 4; break;
+ }
+ h /= 6;
+ }
+
+ return {
+ h: Math.round(h * 360),
+ s: Math.round(s * 100),
+ v: Math.round(v * 100)
+ };
+};
+
+const rgbToCmyk = (r, g, b) => {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ const k = 1 - Math.max(r, g, b);
+ const c = k === 1 ? 0 : (1 - r - k) / (1 - k);
+ const m = k === 1 ? 0 : (1 - g - k) / (1 - k);
+ const y = k === 1 ? 0 : (1 - b - k) / (1 - k);
+
+ return {
+ c: Math.round(c * 100),
+ m: Math.round(m * 100),
+ y: Math.round(y * 100),
+ k: Math.round(k * 100)
+ };
+};
+
+// Generate code snippets for various languages
+const generateCodeSnippets = (r, g, b, a, hsl, hsv) => {
+ const rf = (r / 255).toFixed(3);
+ const gf = (g / 255).toFixed(3);
+ const bf = (b / 255).toFixed(3);
+ const af = a.toFixed(2);
+ const hue = hsl.h;
+ const sat = hsv.s;
+ const bright = hsv.v;
+
+ return {
+ css: [
+ { name: 'RGB', code: `rgb(${r} ${g} ${b})` },
+ { name: 'RGBA', code: `rgb(${r} ${g} ${b} / ${Math.round(a * 100)}%)` },
+ { name: 'HSL', code: `hsl(${hue}deg ${hsl.s}% ${hsl.l}%)` },
+ { name: 'HSLA', code: `hsl(${hue}deg ${hsl.s}% ${hsl.l}% / ${Math.round(a * 100)}%)` },
+ { name: 'Hex', code: rgbToHex(r, g, b, a) },
+ { name: 'CSS Variable', code: `--color-primary: ${rgbToHex(r, g, b, a)};` }
+ ],
+ swift: [
+ { name: 'NSColor RGB', code: `NSColor(
+ calibratedRed: ${rf},
+ green: ${gf},
+ blue: ${bf},
+ alpha: ${af}
+)` },
+ { name: 'NSColor HSB', code: `NSColor(
+ calibratedHue: ${hue / 360},
+ saturation: ${(sat / 100).toFixed(3)},
+ brightness: ${(bright / 100).toFixed(3)},
+ alpha: ${af}
+)` },
+ { name: 'UIColor RGB', code: `UIColor(
+ red: ${rf},
+ green: ${gf},
+ blue: ${bf},
+ alpha: ${af}
+)` },
+ { name: 'UIColor HSB', code: `UIColor(
+ hue: ${hue / 360},
+ saturation: ${(sat / 100).toFixed(3)},
+ brightness: ${(bright / 100).toFixed(3)},
+ alpha: ${af}
+)` }
+ ],
+ dotnet: [
+ { name: 'FromRgb', code: `Color.FromRgb(${r}, ${g}, ${b})` },
+ { name: 'FromArgb', code: `Color.FromArgb(${Math.round(a * 255)}, ${r}, ${g}, ${b})` },
+ { name: 'FromHex', code: `Color.FromHex("${rgbToHex(r, g, b).replace('#', '')}")` }
+ ],
+ java: [
+ { name: 'Color RGB', code: `new Color(${r}, ${g}, ${b})` },
+ { name: 'Color RGBA', code: `new Color(${r}, ${g}, ${b}, ${Math.round(a * 255)})` },
+ { name: 'Color HSB', code: `Color.getHSBColor(${hue / 360}f, ${(sat / 100).toFixed(3)}f, ${(bright / 100).toFixed(3)}f)` }
+ ],
+ android: [
+ { name: 'Color.rgb', code: `Color.rgb(${r}, ${g}, ${b})` },
+ { name: 'Color.argb', code: `Color.argb(${Math.round(a * 255)}, ${r}, ${g}, ${b})` },
+ { name: 'Color.parseColor', code: `Color.parseColor("${rgbToHex(r, g, b)}")` },
+ { name: 'Resource', code: `${rgbToHex(r, g, b)}` },
+ { name: 'Resource with Alpha', code: `${rgbToHex(r, g, b, a)}` }
+ ],
+ opengl: [
+ { name: 'glColor3f', code: `glColor3f(${rf}f, ${gf}f, ${bf}f)` },
+ { name: 'glColor4f', code: `glColor4f(${rf}f, ${gf}f, ${bf}f, ${af}f)` },
+ { name: 'glColor3ub', code: `glColor3ub(${r}, ${g}, ${b})` },
+ { name: 'glColor4ub', code: `glColor4ub(${r}, ${g}, ${b}, ${Math.round(a * 255)})` }
+ ],
+ objc: [
+ { name: 'UIColor RGB', code: `[UIColor colorWithRed:${rf} green:${gf} blue:${bf} alpha:${af}]` },
+ { name: 'UIColor HSB', code: `[UIColor colorWithHue:${(hue / 360).toFixed(3)} saturation:${(sat / 100).toFixed(3)} brightness:${(bright / 100).toFixed(3)} alpha:${af}]` },
+ { name: 'NSColor RGB', code: `[[NSColor colorWithCalibratedRed:${rf} green:${gf} blue:${bf} alpha:${af}]` }
+ ],
+ flutter: [
+ { name: 'Color from RGB', code: `Color.fromRGBO(${r}, ${g}, ${b}, ${af})` },
+ { name: 'Color from ARGB', code: `Color.fromARGB(${Math.round(a * 255)}, ${r}, ${g}, ${b})` },
+ { name: 'Color hex', code: `Color(0xFF${rgbToHex(r, g, b).replace('#', '').toUpperCase()})` },
+ { name: 'Color hex with alpha', code: `Color(0x${Math.round(a * 255).toString(16).padStart(2, '0').toUpperCase()}${rgbToHex(r, g, b).replace('#', '').toUpperCase()})` }
+ ],
+ unity: [
+ { name: 'Color', code: `new Color(${rf}f, ${gf}f, ${bf}f, ${af}f)` },
+ { name: 'Color32', code: `new Color32(${r}, ${g}, ${b}, ${Math.round(a * 255)})` },
+ { name: 'Hex String', code: `ColorUtility.TryParseHtmlString("${rgbToHex(r, g, b)}", out Color color)` }
+ ],
+ reactnative: [
+ { name: 'StyleSheet', code: `const styles = StyleSheet.create({
+ container: {
+ backgroundColor: '${rgbToHex(r, g, b)}',
+ },
+});` },
+ { name: 'Inline', code: `{ backgroundColor: '${rgbToHex(r, g, b)}' }` },
+ { name: 'RGBA', code: `{ backgroundColor: 'rgba(${r}, ${g}, ${b}, ${af})' }` }
+ ],
+ svg: [
+ { name: 'Fill', code: `fill="${rgbToHex(r, g, b)}"` },
+ { name: 'Stroke', code: `stroke="${rgbToHex(r, g, b)}"` },
+ { name: 'Fill with Opacity', code: `fill="${rgbToHex(r, g, b)}" fill-opacity="${af}"` }
+ ]
+ };
+};
+
+// Initial state
+const initialState = {
+ hex: '#3DD6F5',
+ rgb: { r: 61, g: 214, b: 245, a: 1 },
+ hsl: { h: 191, s: 90, l: 60 },
+ hsv: { h: 191, s: 75, v: 96 },
+ cmyk: { c: 75, m: 13, y: 0, k: 4 },
+ history: [],
+ selectedTab: 0
+};
+
+// Reducer for state management
+function colorReducer(state, action) {
+ switch (action.type) {
+ case 'SET_COLOR':
+ return {
+ ...state,
+ ...action.payload
+ };
+ case 'ADD_TO_HISTORY':
+ const newEntry = action.payload;
+ if (state.history.some(h => h.hex === newEntry.hex)) {
+ return state;
+ }
+ return {
+ ...state,
+ history: [newEntry, ...state.history].slice(0, 10)
+ };
+ case 'SET_SELECTED_TAB':
+ return { ...state, selectedTab: action.payload };
+ case 'CLEAR_HISTORY':
+ return { ...state, history: [] };
+ case 'LOAD_FROM_HISTORY':
+ return { ...state, ...action.payload };
+ default:
+ return state;
+ }
+}
+
+export default function ColorConverter() {
+ const [state, dispatch] = useReducer(colorReducer, initialState);
+ const [hexInput, setHexInput] = useState(initialState.hex);
+ const [isHexValid, setIsHexValid] = useState(true);
+ const [rgbInputs, setRgbInputs] = useState(initialState.rgb);
+ const [hslInputs, setHslInputs] = useState(initialState.hsl);
+ const [eyedropperSupported, setEyedropperSupported] = useState(false);
+ const [isPicking, setIsPicking] = useState(false);
+ const historyTimeoutRef = useRef(null);
+
+ // Layout toggle support
+ const layout = useLayoutToggle({
+ toolKey: 'color-converter-layout',
+ defaultDirection: 'horizontal',
+ showToggle: true,
+ persist: true
+ });
+
+ // Check for EyeDropper API support
+ useEffect(() => {
+ setEyedropperSupported('EyeDropper' in window);
+ }, []);
+
+ // Cleanup timeout on unmount
+ useEffect(() => {
+ return () => {
+ if (historyTimeoutRef.current) {
+ clearTimeout(historyTimeoutRef.current);
+ }
+ };
+ }, []);
+
+ // Generate code snippets when color changes
+ const codeSnippets = useMemo(() =>
+ generateCodeSnippets(
+ state.rgb.r, state.rgb.g, state.rgb.b, state.rgb.a,
+ state.hsl, state.hsv
+ ),
+ [state.rgb, state.hsl, state.hsv]
+ );
+
+ // Debounced history recording
+ const debouncedAddToHistory = useCallback((hex, rgb) => {
+ // Clear existing timeout
+ if (historyTimeoutRef.current) {
+ clearTimeout(historyTimeoutRef.current);
+ }
+ // Set new timeout to add to history after 100ms of no changes
+ historyTimeoutRef.current = setTimeout(() => {
+ dispatch({
+ type: 'ADD_TO_HISTORY',
+ payload: { hex, rgb }
+ });
+ }, 100);
+ }, []);
+
+ // Update all color formats from RGB
+ const updateFromRgb = useCallback((r, g, b, a = 1) => {
+ const hex = rgbToHex(r, g, b, a);
+ const hsl = rgbToHsl(r, g, b);
+ const hsv = rgbToHsv(r, g, b);
+ const cmyk = rgbToCmyk(r, g, b);
+
+ dispatch({
+ type: 'SET_COLOR',
+ payload: { hex, rgb: { r, g, b, a }, hsl, hsv, cmyk }
+ });
+ setHexInput(hex);
+ setRgbInputs({ r, g, b, a });
+ setHslInputs(hsl);
+
+ // Debounce history recording
+ debouncedAddToHistory(hex, { r, g, b, a });
+ }, [debouncedAddToHistory]);
+
+ // Handle hex input change
+ const handleHexChange = useCallback((value) => {
+ setHexInput(value);
+ const rgb = hexToRgb(value);
+ if (rgb) {
+ setIsHexValid(true);
+ updateFromRgb(rgb.r, rgb.g, rgb.b, rgb.a);
+ } else {
+ setIsHexValid(false);
+ }
+ }, [updateFromRgb]);
+
+ // Handle hex input blur - validate and reset if invalid
+ const handleHexBlur = useCallback(() => {
+ const rgb = hexToRgb(hexInput);
+ if (!rgb) {
+ // Reset to current valid color
+ setHexInput(state.hex);
+ setIsHexValid(true);
+ }
+ }, [hexInput, state.hex]);
+
+ // Handle RGB input changes
+ const handleRgbChange = useCallback((key, value) => {
+ const numValue = key === 'a'
+ ? parseFloat(value) || 0 // Preserve decimal for alpha
+ : parseInt(value, 10) || 0;
+ const newRgb = { ...rgbInputs, [key]: numValue };
+ setRgbInputs(newRgb);
+
+ if (key === 'a') {
+ updateFromRgb(newRgb.r, newRgb.g, newRgb.b, numValue);
+ } else {
+ updateFromRgb(newRgb.r, newRgb.g, newRgb.b, newRgb.a);
+ }
+ }, [rgbInputs, updateFromRgb]);
+
+ // Handle HSL input changes
+ const handleHslChange = useCallback((key, value) => {
+ const numValue = parseInt(value, 10) || 0;
+ const newHsl = { ...hslInputs, [key]: numValue };
+ setHslInputs(newHsl);
+
+ // Convert HSL to RGB
+ const h = newHsl.h / 360;
+ const s = newHsl.s / 100;
+ const l = newHsl.l / 100;
+
+ let r, g, b;
+
+ if (s === 0) {
+ r = g = b = l;
+ } else {
+ const hue2rgb = (p, q, t) => {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1/6) return p + (q - p) * 6 * t;
+ if (t < 1/2) return q;
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ };
+
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1/3);
+ }
+
+ updateFromRgb(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), state.rgb.a);
+ }, [hslInputs, state.rgb.a, updateFromRgb]);
+
+ // Handle native color picker change
+ const handleColorPickerChange = useCallback((e) => {
+ handleHexChange(e.target.value);
+ }, [handleHexChange]);
+
+ // EyeDropper functionality
+ const openEyeDropper = useCallback(async () => {
+ if (!eyedropperSupported) return;
+
+ setIsPicking(true);
+ try {
+ const eyeDropper = new window.EyeDropper();
+ const result = await eyeDropper.open();
+ if (result.sRGBHex) {
+ handleHexChange(result.sRGBHex);
+ }
+ } catch (e) {
+ // User cancelled or error
+ } finally {
+ setIsPicking(false);
+ }
+ }, [eyedropperSupported, handleHexChange]);
+
+ // Copy to clipboard
+ const copyToClipboard = useCallback((text) => {
+ navigator.clipboard.writeText(text);
+ }, []);
+
+ // Load color from history
+ const loadFromHistory = useCallback((item) => {
+ const rgb = hexToRgb(item.hex);
+ if (rgb) {
+ updateFromRgb(rgb.r, rgb.g, rgb.b, rgb.a);
+ }
+ }, [updateFromRgb]);
+
+ // Generate random color
+ const generateRandomColor = useCallback(() => {
+ const r = Math.floor(Math.random() * 256);
+ const g = Math.floor(Math.random() * 256);
+ const b = Math.floor(Math.random() * 256);
+ updateFromRgb(r, g, b, 1);
+ }, [updateFromRgb]);
+
+ const languageTabs = [
+ { id: 'css', label: 'CSS' },
+ { id: 'swift', label: 'Swift' },
+ { id: 'dotnet', label: '.NET' },
+ { id: 'java', label: 'Java' },
+ { id: 'android', label: 'Android' },
+ { id: 'objc', label: 'Obj-C' },
+ { id: 'flutter', label: 'Flutter' },
+ { id: 'unity', label: 'Unity' },
+ { id: 'reactnative', label: 'React Native' },
+ { id: 'opengl', label: 'OpenGL' },
+ { id: 'svg', label: 'SVG' }
+ ];
+
+ return (
+
+
+
+
+ {/* Color Preview & Picker */}
+
+
+
+ {eyedropperSupported && (
+
+ )}
+
+
+ {/* Input Controls */}
+
+ {/* Hex Input */}
+
+
+ handleHexChange(e.target.value)}
+ onBlur={handleHexBlur}
+ placeholder="#3DD6F5"
+ style={{ fontFamily: "'IBM Plex Mono', monospace" }}
+ size="sm"
+ invalid={!isHexValid}
+ invalidText="Invalid hex color"
+ />
+
+
+ {/* RGB Inputs */}
+
+
+
+ handleRgbChange('r', e.target.value)}
+ size="sm"
+ />
+
+
+
+ handleRgbChange('g', e.target.value)}
+ size="sm"
+ />
+
+
+
+ handleRgbChange('b', e.target.value)}
+ size="sm"
+ />
+
+
+
+ handleRgbChange('a', e.target.value)}
+ size="sm"
+ />
+
+
+
+ {/* HSL Inputs */}
+
+
+
+ handleHslChange('h', e.target.value)}
+ size="sm"
+ />
+
+
+
+ handleHslChange('s', e.target.value)}
+ size="sm"
+ />
+
+
+
+ handleHslChange('l', e.target.value)}
+ size="sm"
+ />
+
+
+
+
+ {/* Layout Toggle */}
+
+
+
+
+
+ {/* Format Values */}
+
+
+ RGB:
+
+ {state.rgb.r}, {state.rgb.g}, {state.rgb.b}
+
+
+
+ HEX:
+
+ {state.hex}
+
+
+
+ HSL:
+
+ {state.hsl.h}°, {state.hsl.s}%, {state.hsl.l}%
+
+
+
+ HSV:
+
+ {state.hsv.h}°, {state.hsv.s}%, {state.hsv.v}%
+
+
+
+ CMYK:
+
+ {state.cmyk.c}%, {state.cmyk.m}%, {state.cmyk.y}%, {state.cmyk.k}%
+
+
+
+
+ {/* Main Content Area */}
+
+ {/* Color History */}
+ {state.history.length > 0 && (
+
+
+ History
+
+
+ {state.history.map((item, idx) => (
+
loadFromHistory(item)}
+ style={{
+ display: 'flex',
+ alignItems: 'center',
+ gap: '0.5rem',
+ padding: '0.5rem',
+ cursor: 'pointer',
+ borderRadius: '4px',
+ marginBottom: '0.25rem',
+ backgroundColor: 'var(--cds-layer-hover)'
+ }}
+ >
+
+
+ {item.hex}
+
+
+ ))}
+
+
+ )}
+
+ {/* Code Snippets */}
+
+
dispatch({ type: 'SET_SELECTED_TAB', payload: selectedIndex })}
+ >
+
+ {languageTabs.map(tab => (
+ {tab.label}
+ ))}
+
+
+ {languageTabs.map(tab => (
+
+
+ {(codeSnippets[tab.id] || []).map((snippet, idx) => (
+
+
+
+
+ {snippet.name}
+
+
+ {snippet.code}
+
+
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+ );
+}