MVP complete
This commit is contained in:
@ -7,7 +7,7 @@ interface ColorPaletteProps {
|
||||
|
||||
const ColorPalette: React.FC<ColorPaletteProps> = ({ colors, size = 'small' }) => {
|
||||
const [copiedColor, setCopiedColor] = useState<string | null>(null);
|
||||
const [hoveredColor, setHoveredColor] = useState<string | null>(null);
|
||||
const [hoveredColorId, setHoveredColorId] = useState<string | null>(null);
|
||||
const textAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const handleColorClick = (color: string) => {
|
||||
@ -39,33 +39,36 @@ const ColorPalette: React.FC<ColorPaletteProps> = ({ colors, size = 'small' }) =
|
||||
return (
|
||||
<>
|
||||
<div className={`grid grid-cols-8 gap-2 ${size === 'large' ? 'mb-4' : 'mb-2'} z-10`}>
|
||||
{colors.map((color, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`${sizeClasses} rounded-sm cursor-pointer relative group`}
|
||||
style={{backgroundColor: color}}
|
||||
onClick={() => handleColorClick(color)}
|
||||
onMouseEnter={() => setHoveredColor(color)}
|
||||
onMouseLeave={() => setHoveredColor(null)}
|
||||
>
|
||||
{size === 'small' && hoveredColor === color && (
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-white dark:bg-gray-800 rounded shadow-lg z-10">
|
||||
<div className="w-4 h-4 rounded-sm mb-1" style={{backgroundColor: color}}></div>
|
||||
<span className="text-xs">{color}</span>
|
||||
</div>
|
||||
)}
|
||||
{size === 'large' && (
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-black bg-opacity-50 text-white text-[8px]">
|
||||
{color}
|
||||
</div>
|
||||
)}
|
||||
{copiedColor === color && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-70 text-white text-xs">
|
||||
Copied!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{colors.map((color, index) => {
|
||||
const colorId = `color-${index}-${color}`;
|
||||
return (
|
||||
<div
|
||||
key={colorId}
|
||||
className={`${sizeClasses} rounded-sm cursor-pointer relative group`}
|
||||
style={{backgroundColor: color}}
|
||||
onClick={() => handleColorClick(color)}
|
||||
onMouseEnter={() => setHoveredColorId(colorId)}
|
||||
onMouseLeave={() => setHoveredColorId(null)}
|
||||
>
|
||||
{size === 'small' && hoveredColorId === colorId && (
|
||||
<div className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-white dark:bg-gray-800 rounded shadow-lg z-10">
|
||||
<div className="w-4 h-4 rounded-sm mb-1" style={{backgroundColor: color}}></div>
|
||||
<span className="text-xs">{color}</span>
|
||||
</div>
|
||||
)}
|
||||
{size === 'large' && (
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-black bg-opacity-50 text-white text-[8px]">
|
||||
{color}
|
||||
</div>
|
||||
)}
|
||||
{copiedColor === color && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black bg-opacity-70 text-white text-xs">
|
||||
Copied!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<textarea
|
||||
ref={textAreaRef}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import { ColorScheme } from '../utils/colorSchemes';
|
||||
import { generateYAML } from '../utils/yamlExport';
|
||||
import { generateYAML, generateJSON, generateXResources, generateTOML } from '../utils/exportFormats';
|
||||
import { Highlight, themes } from 'prism-react-renderer';
|
||||
import { motion, useAnimation } from 'framer-motion';
|
||||
import ColorPalette from './ColorPalette';
|
||||
@ -12,49 +12,238 @@ interface ColorSchemeCardProps {
|
||||
onDislike: () => void;
|
||||
index: number;
|
||||
isDarkMode: boolean;
|
||||
codeSample: string;
|
||||
outputFormat: string;
|
||||
}
|
||||
|
||||
const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, onLike, onDislike, index, isDarkMode }) => {
|
||||
const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, onLike, onDislike, index, isDarkMode, codeSample, outputFormat }) => {
|
||||
const [overlayColor, setOverlayColor] = useState('rgba(0, 0, 0, 0)');
|
||||
const controls = useAnimation();
|
||||
|
||||
const codeExample = `
|
||||
// User object and function
|
||||
const user = {
|
||||
name: 'DWS',
|
||||
power: 8999
|
||||
};
|
||||
const getCodeExample = () => {
|
||||
// Add code samples for each language here
|
||||
const samples = {
|
||||
c: `#include <stdio.h>
|
||||
|
||||
class Shape {
|
||||
constructor(color) {
|
||||
this.color = color;
|
||||
int is_prime(int n) {
|
||||
if (n <= 1) return 0;
|
||||
for (int i = 2; i * i <= n; i++) {
|
||||
if (n % i == 0) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Async data fetch simulation
|
||||
async function fetchData() {
|
||||
return await new Promise(resolve => setTimeout(() => resolve('Data loaded'), 500));
|
||||
void print_primes(int limit) {
|
||||
printf("Primes up to %d:\n", limit);
|
||||
for (int i = 2; i <= limit; i++) {
|
||||
if (is_prime(i)) {
|
||||
printf("%d ", i);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
const [even, odd] = [2, 4, 6, 8].reduce(([e, o], n) => n % 2 ? [e, [...o, n]] : [
|
||||
[...e, n], o
|
||||
], [
|
||||
[],
|
||||
[]
|
||||
]);
|
||||
|
||||
/*
|
||||
Logging here
|
||||
*/
|
||||
fetchData().then(data => console.log(data)); `;
|
||||
int main() {
|
||||
int limit = 50;
|
||||
print_primes(limit);
|
||||
|
||||
int num = 7;
|
||||
printf("Is %d prime? %s\n", num, is_prime(num) ? "Yes" : "No");
|
||||
return 0;
|
||||
}`,
|
||||
python: `import numpy as np
|
||||
|
||||
@timeit
|
||||
def is_prime(n):
|
||||
if n <= 1:
|
||||
return False
|
||||
for i in range(2, int(n ** 0.5) + 1):
|
||||
if n % i == 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def primes_up_to(limit):
|
||||
"""
|
||||
This function returns a list of prime numbers up to a given limit.
|
||||
"""
|
||||
primes = []
|
||||
for i in range(2, limit + 1):
|
||||
if is_prime(i):
|
||||
primes.append(i)
|
||||
return primes
|
||||
|
||||
def fibonacci(n):
|
||||
fib_sequence = [0, 1] # Create a list with the first two numbers in the Fibonacci sequence
|
||||
for i in range(2, n):
|
||||
fib_sequence.append(fib_sequence[-1] + fib_sequence[-2])
|
||||
return fib_sequence
|
||||
|
||||
limit = 50
|
||||
print(f"Primes up to {limit}: {primes_up_to(limit)}")
|
||||
print(f"Fibonacci sequence up to 10: {fibonacci(10)}")
|
||||
`,
|
||||
rust: `fn is_prime(n: u32) -> bool {
|
||||
if n <= 1 {
|
||||
return false;
|
||||
}
|
||||
for i in 2..=((n as f64).sqrt() as u32) {
|
||||
if n % i == 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn primes_up_to(limit: u32) -> Vec<u32> {
|
||||
let mut primes = Vec::new();
|
||||
for i in 2..=limit {
|
||||
if is_prime(i) {
|
||||
primes.push(i);
|
||||
}
|
||||
}
|
||||
primes
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let limit = 50;
|
||||
let primes = primes_up_to(limit);
|
||||
println!("Primes up to {}: {:?}", limit, primes);
|
||||
|
||||
let num = 7;
|
||||
println!("Is {} prime? {}", num, is_prime(num));
|
||||
}`,
|
||||
go: `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func isPrime(n int) bool {
|
||||
if n <= 1 {
|
||||
return false
|
||||
}
|
||||
for i := 2; i <= int(math.Sqrt(float64(n))); i++ {
|
||||
if n%i == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func primesUpTo(limit int) []int {
|
||||
primes := []int{}
|
||||
for i := 2; i <= limit; i++ {
|
||||
if isPrime(i) {
|
||||
primes = append(primes, i)
|
||||
}
|
||||
}
|
||||
return primes
|
||||
}
|
||||
|
||||
func main() {
|
||||
limit := 50
|
||||
fmt.Printf("Primes up to %d: %v\n", limit, primesUpTo(limit))
|
||||
|
||||
num := 7
|
||||
fmt.Printf("Is %d prime? %v\n", num, isPrime(num))
|
||||
}`,
|
||||
javascript: `function isPrime(n) {
|
||||
if (n <= 1) return false;
|
||||
for (let i = 2; i <= Math.sqrt(n); i++) {
|
||||
if (n % i === 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function primesUpTo(limit) {
|
||||
let primes = [];
|
||||
for (let i = 2; i <= limit; i++) {
|
||||
if (isPrime(i)) primes.push(i);
|
||||
}
|
||||
return primes;
|
||||
}
|
||||
|
||||
function fibonacci(n) {
|
||||
let fib = [0, 1];
|
||||
for (let i = 2; i < n; i++) {
|
||||
fib.push(fib[i - 1] + fib[i - 2]);
|
||||
}
|
||||
return fib;
|
||||
}
|
||||
|
||||
let limit = 50;`,
|
||||
bash: `#!/bin/bash
|
||||
|
||||
is_prime() {
|
||||
local n=$1
|
||||
if [ $n -le 1 ]; then
|
||||
echo 0
|
||||
return
|
||||
fi
|
||||
for ((i=2; i*i<=n; i++)); do
|
||||
if ((n % i == 0)); then
|
||||
echo 0
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo 1
|
||||
}
|
||||
|
||||
primes_up_to() {
|
||||
local limit=$1
|
||||
for ((i=2; i<=limit; i++)); do
|
||||
if [ $(is_prime $i) -eq 1 ]; then
|
||||
echo -n "$i "
|
||||
fi
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
limit=50
|
||||
echo "Primes up to $limit: $(primes_up_to $limit)"
|
||||
|
||||
num=7
|
||||
if [ $(is_prime $num) -eq 1 ]; then
|
||||
echo "$num is prime"
|
||||
else
|
||||
echo "$num is not prime"
|
||||
fi`
|
||||
};
|
||||
|
||||
return samples[codeSample] || samples.javascript;
|
||||
};
|
||||
|
||||
const handleDownload = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const yaml = generateYAML(scheme);
|
||||
const blob = new Blob([yaml], { type: 'text/yaml' });
|
||||
let content: string;
|
||||
let fileExtension: string;
|
||||
|
||||
switch (outputFormat) {
|
||||
case 'json':
|
||||
content = generateJSON(scheme);
|
||||
fileExtension = 'json';
|
||||
break;
|
||||
case 'xresources':
|
||||
content = generateXResources(scheme);
|
||||
fileExtension = 'Xresources';
|
||||
break;
|
||||
case 'toml':
|
||||
content = generateTOML(scheme);
|
||||
fileExtension = 'toml';
|
||||
break;
|
||||
case 'yaml':
|
||||
default:
|
||||
content = generateYAML(scheme);
|
||||
fileExtension = 'yaml';
|
||||
}
|
||||
|
||||
const blob = new Blob([content], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.yaml`;
|
||||
a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.${fileExtension}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
@ -71,55 +260,114 @@ fetchData().then(data => console.log(data)); `;
|
||||
controls.start({ x: -300, opacity: 0, transition: { duration: 0.3 } }).then(onDislike);
|
||||
};
|
||||
|
||||
const paletteColors = [
|
||||
scheme.colors.normal.black,
|
||||
scheme.colors.normal.red,
|
||||
scheme.colors.normal.green,
|
||||
scheme.colors.normal.yellow,
|
||||
scheme.colors.normal.blue,
|
||||
scheme.colors.normal.magenta,
|
||||
scheme.colors.normal.cyan,
|
||||
scheme.colors.normal.white,
|
||||
scheme.colors.bright.black,
|
||||
scheme.colors.bright.red,
|
||||
scheme.colors.bright.green,
|
||||
scheme.colors.bright.yellow,
|
||||
scheme.colors.bright.blue,
|
||||
scheme.colors.bright.magenta,
|
||||
scheme.colors.bright.cyan,
|
||||
scheme.colors.bright.white,
|
||||
];
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="absolute w-[350px] h-[600px] bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
|
||||
className="absolute w-[80vw] max-w-[480px] h-[90vh] max-h-[600px] bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden"
|
||||
initial={{ scale: 1 - index * 0.05, y: index * 15, opacity: 1 }}
|
||||
animate={controls}
|
||||
style={{
|
||||
zIndex: 3 - index
|
||||
}}
|
||||
drag="x"
|
||||
dragConstraints={{ left: -100, right: 100 }}
|
||||
dragConstraints={{ left: -50, right: 50 }}
|
||||
onDragEnd={(e, { offset, velocity }) => {
|
||||
if (offset.x > 100) handleLike();
|
||||
else if (offset.x < -100) handleDislike();
|
||||
if (offset.x > 50) handleLike();
|
||||
else if (offset.x < -50) handleDislike();
|
||||
}}
|
||||
>
|
||||
<div className="p-6 h-full flex flex-col relative">
|
||||
<div className="p-4 h-full flex flex-col relative">
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-lg"
|
||||
animate={{ backgroundColor: overlayColor }}
|
||||
initial={{ backgroundColor: 'rgba(0, 0, 0, 0)' }}
|
||||
transition={{ duration: 0.2 }}
|
||||
/>
|
||||
<div className="flex justify-between items-center mb-4 z-10">
|
||||
<h2 className="text-xl font-semibold">{scheme.name}</h2>
|
||||
<div className="flex justify-between items-center mb-2 z-10">
|
||||
<h2 className="text-lg font-semibold truncate">{scheme.name}</h2>
|
||||
<button
|
||||
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors duration-300"
|
||||
onClick={handleDownload}
|
||||
>
|
||||
<Image src={isDarkMode ? "/download-icon-dark.svg" : "/download-icon-light.svg"} alt="Download" width={24} height={24} />
|
||||
<Image src={isDarkMode ? "/download-icon-dark.svg" : "/download-icon-light.svg"} alt="Download" width={20} height={20} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-gray-100 dark:bg-gray-700 rounded-md mb-4 flex-grow overflow-hidden z-10 shadow-md">
|
||||
<Highlight theme={themes.dracula} code={codeExample} language="javascript">
|
||||
<div className="bg-gray-100 dark:bg-gray-700 rounded-md mb-2 flex-grow overflow-hidden z-10 shadow-md">
|
||||
<Highlight theme={themes.dracula} code={getCodeExample()} language={codeSample}>
|
||||
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
||||
<pre className={`${className} text-xs p-4 overflow-x-auto`} style={{ ...style, backgroundColor: scheme.colors.primary.background }}>
|
||||
{tokens.slice(0, 20).map((line, i) => (
|
||||
<pre className={`${className} text-sm p-4 h-full overflow-auto`} style={{ ...style, backgroundColor: scheme.colors.primary.background }}>
|
||||
{tokens.map((line, i) => (
|
||||
<div key={i} {...getLineProps({ line, key: i })}>
|
||||
{line.map((token, key) => {
|
||||
let color = scheme.colors.primary.foreground;
|
||||
if (token.types.includes('keyword')) color = scheme.colors.normal.blue;
|
||||
if (token.types.includes('string')) color = scheme.colors.normal.green;
|
||||
if (token.types.includes('number')) color = scheme.colors.normal.magenta;
|
||||
if (token.types.includes('comment')) color = scheme.colors.bright.black;
|
||||
if (token.types.includes('function')) color = scheme.colors.normal.yellow;
|
||||
if (token.types.includes('operator')) color = scheme.colors.normal.cyan;
|
||||
if (token.types.includes('class-name')) color = scheme.colors.bright.magenta;
|
||||
if (token.types.includes('constant')) color = scheme.colors.bright.red;
|
||||
if (token.types.includes('punctuation')) color = scheme.colors.bright.cyan;
|
||||
if (token.types.includes('variable')) color = scheme.colors.bright.yellow;
|
||||
let color = scheme.colors.primary.foreground;
|
||||
if (token.types.includes('keyword')) color = scheme.colors.normal.blue;
|
||||
if (token.types.includes('string')) color = scheme.colors.normal.green;
|
||||
if (token.types.includes('number')) color = scheme.colors.normal.magenta;
|
||||
if (token.types.includes('comment')) color = scheme.colors.bright.black;
|
||||
if (token.types.includes('function')) color = scheme.colors.normal.yellow;
|
||||
if (token.types.includes('operator')) color = scheme.colors.normal.cyan;
|
||||
if (token.types.includes('class-name')) color = scheme.colors.bright.magenta;
|
||||
if (token.types.includes('constant')) color = scheme.colors.bright.red;
|
||||
if (token.types.includes('punctuation')) color = scheme.colors.bright.cyan;
|
||||
if (token.types.includes('variable')) color = scheme.colors.bright.yellow;
|
||||
if (token.types.includes('boolean')) color = scheme.colors.bright.yellow;
|
||||
if (token.types.includes('builtin')) color = scheme.colors.bright.magenta;
|
||||
if (token.types.includes('attr-name')) color = scheme.colors.normal.red;
|
||||
if (token.types.includes('attr-value')) color = scheme.colors.bright.green;
|
||||
if (token.types.includes('tag')) color = scheme.colors.normal.blue;
|
||||
if (token.types.includes('namespace')) color = scheme.colors.normal.magenta;
|
||||
if (token.types.includes('selector')) color = scheme.colors.normal.yellow;
|
||||
if (token.types.includes('property')) color = scheme.colors.normal.cyan;
|
||||
if (token.types.includes('unit')) color = scheme.colors.bright.yellow;
|
||||
if (token.types.includes('important')) color = scheme.colors.bright.red;
|
||||
if (token.types.includes('symbol')) color = scheme.colors.normal.magenta; // Symbols (e.g., special characters)
|
||||
if (token.types.includes('regex')) color = scheme.colors.bright.green; // Regular expressions
|
||||
if (token.types.includes('template-string')) color = scheme.colors.normal.green; // Template literals
|
||||
if (token.types.includes('char')) color = scheme.colors.bright.yellow; // Individual characters
|
||||
if (token.types.includes('module')) color = scheme.colors.normal.cyan; // Modules
|
||||
if (token.types.includes('directive')) color = scheme.colors.normal.magenta; // Directives (e.g., preprocessor)
|
||||
if (token.types.includes('annotation')) color = scheme.colors.normal.green; // Annotations (e.g., decorators)
|
||||
if (token.types.includes('parameter')) color = scheme.colors.bright.yellow; // Parameters in functions
|
||||
if (token.types.includes('method')) color = scheme.colors.normal.yellow; // Methods
|
||||
if (token.types.includes('field')) color = scheme.colors.bright.blue; // Fields within classes
|
||||
if (token.types.includes('property-access')) color = scheme.colors.normal.cyan; // Property accessors (e.g., dot notation)
|
||||
if (token.types.includes('escape')) color = scheme.colors.bright.red; // Escape sequences
|
||||
if (token.types.includes('meta')) color = scheme.colors.bright.magenta; // Meta information (e.g., HTML meta tags)
|
||||
if (token.types.includes('label')) color = scheme.colors.bright.yellow; // Labels (e.g., in loops)
|
||||
if (token.types.includes('alias')) color = scheme.colors.normal.magenta; // Aliases (e.g., imports or type aliases)
|
||||
if (token.types.includes('error')) color = scheme.colors.bright.red; // Error handling, error literals
|
||||
if (token.types.includes('debug')) color = scheme.colors.bright.magenta; // Debugging statements (e.g., console logs)
|
||||
if (token.types.includes('property-declaration')) color = scheme.colors.normal.blue; // Property declarations in classes
|
||||
if (token.types.includes('module-declaration')) color = scheme.colors.normal.cyan; // Declarations of modules
|
||||
if (token.types.includes('generic')) color = scheme.colors.bright.magenta; // Generics (e.g., <T>)
|
||||
if (token.types.includes('type')) color = scheme.colors.bright.blue; // Types in TypeScript, Flow, etc.
|
||||
if (token.types.includes('interface')) color = scheme.colors.normal.green; // Interfaces (e.g., TypeScript)
|
||||
if (token.types.includes('enums')) color = scheme.colors.bright.cyan; // Enums in languages like TypeScript, Java
|
||||
if (token.types.includes('modifier')) color = scheme.colors.normal.yellow; // Modifiers (e.g., public, static)
|
||||
if (token.types.includes('event')) color = scheme.colors.bright.blue; // Events (e.g., DOM events or custom events)
|
||||
if (token.types.includes('keyword-control')) color = scheme.colors.bright.yellow; // Control-flow keywords (e.g., if, for)
|
||||
if (token.types.includes('loop')) color = scheme.colors.normal.magenta; // Loop keywords (e.g., for, while)
|
||||
if (token.types.includes('storage')) color = scheme.colors.normal.red; // Storage-related keywords (e.g., var, let)
|
||||
if (token.types.includes('annotation-function')) color = scheme.colors.bright.green; // Annotated functions
|
||||
if (token.types.includes('doc-string')) color = scheme.colors.bright.blue; // Docstrings or comments that are part of documentation
|
||||
return <span key={key} {...getTokenProps({ token, key })} style={{ ...getTokenProps({ token, key }).style, color }} />;
|
||||
})}
|
||||
</div>
|
||||
@ -128,22 +376,24 @@ fetchData().then(data => console.log(data)); `;
|
||||
)}
|
||||
</Highlight>
|
||||
</div>
|
||||
<ColorPalette
|
||||
colors={Object.values(scheme.colors.normal).concat(Object.values(scheme.colors.bright))}
|
||||
size="large"
|
||||
/>
|
||||
<div className="flex justify-center space-x-8 mt-4 z-10">
|
||||
<div className="mt-2 z-10">
|
||||
<ColorPalette
|
||||
colors={paletteColors}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-center space-x-4 mt-2 z-10">
|
||||
<button
|
||||
className="bg-red-500 text-white p-3 rounded-full shadow-lg hover:bg-red-600 transition-colors duration-300"
|
||||
className="bg-red-500 text-white p-2 rounded-full shadow-lg hover:bg-red-600 transition-colors duration-300"
|
||||
onClick={handleDislike}
|
||||
>
|
||||
<Image src={isDarkMode ? "/cross-icon-dark.svg" : "/cross-icon-light.svg"} alt="Dislike" width={28} height={28} />
|
||||
<Image src={isDarkMode ? "/cross-icon-dark.svg" : "/cross-icon-light.svg"} alt="Dislike" width={24} height={24} />
|
||||
</button>
|
||||
<button
|
||||
className="bg-green-500 text-white p-3 rounded-full shadow-lg hover:bg-green-600 transition-colors duration-300"
|
||||
className="bg-green-500 text-white p-2 rounded-full shadow-lg hover:bg-green-600 transition-colors duration-300"
|
||||
onClick={handleLike}
|
||||
>
|
||||
<Image src={isDarkMode ? "/heart-icon-dark.svg" : "/heart-icon-light.svg"} alt="Like" width={28} height={28} />
|
||||
<Image src={isDarkMode ? "/heart-icon-dark.svg" : "/heart-icon-light.svg"} alt="Like" width={24} height={24} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ColorScheme } from '../utils/colorSchemes';
|
||||
import { generateYAML } from '../utils/yamlExport';
|
||||
import { generateYAML, generateJSON, generateXResources, generateTOML } from '../utils/exportFormats';
|
||||
import Image from 'next/image';
|
||||
import ColorPalette from './ColorPalette';
|
||||
|
||||
@ -9,18 +9,40 @@ interface HistoryPopupProps {
|
||||
dislikedSchemes: ColorScheme[];
|
||||
onClose: () => void;
|
||||
isDarkMode: boolean;
|
||||
outputFormat: string;
|
||||
}
|
||||
|
||||
const HistoryPopup: React.FC<HistoryPopupProps> = ({ likedSchemes, dislikedSchemes, onClose, isDarkMode }) => {
|
||||
const HistoryPopup: React.FC<HistoryPopupProps> = ({ likedSchemes, dislikedSchemes, onClose, isDarkMode, outputFormat }) => {
|
||||
const [copiedColor, setCopiedColor] = useState<string | null>(null);
|
||||
|
||||
const handleDownload = (scheme: ColorScheme) => {
|
||||
const yaml = generateYAML(scheme);
|
||||
const blob = new Blob([yaml], { type: 'text/yaml' });
|
||||
let content: string;
|
||||
let fileExtension: string;
|
||||
|
||||
switch (outputFormat) {
|
||||
case 'json':
|
||||
content = generateJSON(scheme);
|
||||
fileExtension = 'json';
|
||||
break;
|
||||
case 'xresources':
|
||||
content = generateXResources(scheme);
|
||||
fileExtension = 'Xresources';
|
||||
break;
|
||||
case 'toml':
|
||||
content = generateTOML(scheme);
|
||||
fileExtension = 'toml';
|
||||
break;
|
||||
case 'yaml':
|
||||
default:
|
||||
content = generateYAML(scheme);
|
||||
fileExtension = 'yaml';
|
||||
}
|
||||
|
||||
const blob = new Blob([content], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.yaml`;
|
||||
a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.${fileExtension}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
|
@ -1,41 +1,128 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface SettingsProps {
|
||||
isDarkMode: boolean;
|
||||
onToggleDarkMode: () => void;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
isDarkMode: boolean;
|
||||
onToggleDarkMode: () => void;
|
||||
outputFormat: string;
|
||||
setOutputFormat: (format: string) => void;
|
||||
codeSample: string;
|
||||
setCodeSample: (sample: string) => void;
|
||||
saveSettings: boolean;
|
||||
setSaveSettings: (save: boolean) => void;
|
||||
}
|
||||
|
||||
const Settings: React.FC<SettingsProps> = ({ isDarkMode, onToggleDarkMode, isOpen, onClose }) => {
|
||||
const Settings: React.FC<SettingsProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
isDarkMode,
|
||||
onToggleDarkMode,
|
||||
outputFormat,
|
||||
setOutputFormat,
|
||||
codeSample,
|
||||
setCodeSample,
|
||||
saveSettings,
|
||||
setSaveSettings
|
||||
}) => {
|
||||
const [showCookieNotice, setShowCookieNotice] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (saveSettings && !localStorage.getItem('cookieNoticeShown')) {
|
||||
setShowCookieNotice(true);
|
||||
}
|
||||
}, [saveSettings]);
|
||||
|
||||
const handleSaveSettingsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.checked;
|
||||
setSaveSettings(newValue);
|
||||
if (newValue && !localStorage.getItem('cookieNoticeShown')) {
|
||||
setShowCookieNotice(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCookieNoticeClose = () => {
|
||||
setShowCookieNotice(false);
|
||||
localStorage.setItem('cookieNoticeShown', 'true');
|
||||
};
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl">
|
||||
<h2 className="text-xl font-bold mb-4">Settings</h2>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<span>Dark Mode</span>
|
||||
<button
|
||||
onClick={onToggleDarkMode}
|
||||
className={`w-12 h-6 rounded-full p-1 transition-colors duration-300 ease-in-out ${
|
||||
isDarkMode ? 'bg-blue-600' : 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`w-4 h-4 rounded-full bg-white transform transition-transform duration-300 ease-in-out ${
|
||||
isDarkMode ? 'translate-x-6' : ''
|
||||
}`}
|
||||
/>
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl w-[90vw] max-w-md">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-2xl font-bold">Settings</h2>
|
||||
<button onClick={onClose} className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<Image src={isDarkMode ? "/close-icon-dark.svg" : "/close-icon-light.svg"} alt="Close" width={24} height={24} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="mt-4 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition-colors duration-300"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span>Dark Mode</span>
|
||||
<button
|
||||
onClick={onToggleDarkMode}
|
||||
className={`w-12 h-6 rounded-full p-1 transition-colors duration-300 ease-in-out ${
|
||||
isDarkMode ? 'bg-blue-600' : 'bg-gray-300'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`w-4 h-4 rounded-full bg-white transform transition-transform duration-300 ease-in-out ${
|
||||
isDarkMode ? 'translate-x-6' : ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2">Output Format</label>
|
||||
<select
|
||||
value={outputFormat}
|
||||
onChange={(e) => setOutputFormat(e.target.value)}
|
||||
className="w-full p-2 border rounded dark:bg-gray-700 dark:border-gray-600"
|
||||
>
|
||||
<option value="json">JSON</option>
|
||||
<option value="xresources">XResources</option>
|
||||
<option value="yaml">YAML (Alacritty)</option>
|
||||
<option value="toml">TOML (Alacritty)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block mb-2">Code Sample</label>
|
||||
<select
|
||||
value={codeSample}
|
||||
onChange={(e) => setCodeSample(e.target.value)}
|
||||
className="w-full p-2 border rounded dark:bg-gray-700 dark:border-gray-600"
|
||||
>
|
||||
<option value="c">C</option>
|
||||
<option value="python">Python</option>
|
||||
<option value="rust">Rust</option>
|
||||
<option value="go">Go</option>
|
||||
<option value="javascript">JavaScript</option>
|
||||
<option value="java">Java</option>
|
||||
<option value="bash">Bash</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="saveSettings"
|
||||
checked={saveSettings}
|
||||
onChange={handleSaveSettingsChange}
|
||||
className="mr-2"
|
||||
/>
|
||||
<label htmlFor="saveSettings">Save settings in cookie</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showCookieNotice && (
|
||||
<div className="fixed bottom-0 left-0 right-0 bg-blue-500 text-white p-4">
|
||||
<p>We will save your settings in a cookie. Is this okay?</p>
|
||||
<button onClick={handleCookieNoticeClose} className="mt-2 bg-white text-blue-500 px-4 py-2 rounded">
|
||||
Accept
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
96
app/page.tsx
96
app/page.tsx
@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
import ColorSchemeCard from "./components/ColorSchemeCard";
|
||||
import HistoryPopup from "./components/HistoryPopup";
|
||||
import Settings from "./components/Settings";
|
||||
import { ColorScheme, knownSchemes, generateRandomScheme, generateSchemeFromGeneticAlgorithm } from './utils/colorSchemes';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
|
||||
@ -13,6 +14,10 @@ export default function Home() {
|
||||
const [likedSchemes, setLikedSchemes] = useState<ColorScheme[]>([]);
|
||||
const [dislikedSchemes, setDislikedSchemes] = useState<ColorScheme[]>([]);
|
||||
const [isHistoryOpen, setIsHistoryOpen] = useState(false);
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
const [outputFormat, setOutputFormat] = useState('yaml');
|
||||
const [codeSample, setCodeSample] = useState('javascript');
|
||||
const [saveSettings, setSaveSettings] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
generateNewSchemes(8);
|
||||
@ -25,6 +30,15 @@ export default function Home() {
|
||||
if (storedDislikedSchemes) {
|
||||
setDislikedSchemes(JSON.parse(storedDislikedSchemes));
|
||||
}
|
||||
|
||||
// Load settings from cookie if available
|
||||
const storedSettings = document.cookie.split('; ').find(row => row.startsWith('settings='));
|
||||
if (storedSettings) {
|
||||
const settings = JSON.parse(storedSettings.split('=')[1]);
|
||||
setOutputFormat(settings.outputFormat);
|
||||
setCodeSample(settings.codeSample);
|
||||
setSaveSettings(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@ -40,29 +54,26 @@ export default function Home() {
|
||||
}, [dislikedSchemes]);
|
||||
|
||||
const generateNewSchemes = (count: number) => {
|
||||
const newSchemes = Array(count).fill(null).map(() => {
|
||||
const schemeType = Math.random();
|
||||
if (schemeType < 0.25) {
|
||||
// 25% chance of known scheme
|
||||
return knownSchemes[Math.floor(Math.random() * knownSchemes.length)];
|
||||
} else if (schemeType < 0.5) {
|
||||
// 25% chance of random scheme
|
||||
return generateRandomScheme();
|
||||
} else if (likedSchemes.length > 2 && dislikedSchemes.length > 2) {
|
||||
// 50% chance of genetic scheme if there are more than 2 liked and disliked schemes
|
||||
return generateSchemeFromGeneticAlgorithm(likedSchemes, dislikedSchemes);
|
||||
} else {
|
||||
// 30% known, 20% random for other cases
|
||||
if (Math.random() < 0.3) {
|
||||
return generateRandomScheme();
|
||||
} else {
|
||||
return knownSchemes[Math.floor(Math.random() * knownSchemes.length)];
|
||||
}
|
||||
}
|
||||
});
|
||||
const knownCount = Math.floor(count / 2);
|
||||
const generatedCount = count - knownCount;
|
||||
const newSchemes = [
|
||||
...knownSchemes.sort(() => 0.5 - Math.random()).slice(0, knownCount),
|
||||
...Array(generatedCount).fill(null).map(() =>
|
||||
likedSchemes.length > 0 ? generateSchemeFromGeneticAlgorithm(likedSchemes, dislikedSchemes) : generateRandomScheme()
|
||||
)
|
||||
];
|
||||
|
||||
setSchemes(prevSchemes => [...prevSchemes, ...newSchemes]);
|
||||
setSchemes(prevSchemes => [...prevSchemes, ...newSchemes].sort(() => 0.5 - Math.random()));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (saveSettings) {
|
||||
const settings = JSON.stringify({ outputFormat, codeSample });
|
||||
document.cookie = `settings=${settings}; max-age=31536000; path=/`; // 1 year expiration
|
||||
} else {
|
||||
document.cookie = 'settings=; max-age=0; path=/';
|
||||
}
|
||||
}, [saveSettings, outputFormat, codeSample]);
|
||||
|
||||
const handleLike = (scheme: ColorScheme) => {
|
||||
setLikedSchemes(prev => [...prev, scheme]);
|
||||
@ -92,6 +103,10 @@ export default function Home() {
|
||||
setIsHistoryOpen(!isHistoryOpen);
|
||||
};
|
||||
|
||||
const toggleSettings = () => {
|
||||
setIsSettingsOpen(!isSettingsOpen);
|
||||
};
|
||||
|
||||
const getAllSchemes = () => {
|
||||
const allSchemes = [...likedSchemes, ...dislikedSchemes];
|
||||
const uniqueSchemes = allSchemes.filter((scheme, index, self) =>
|
||||
@ -101,17 +116,19 @@ export default function Home() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen w-screen overflow-hidden font-[family-name:var(--font-geist-sans)] dark:bg-gray-900 dark:text-white transition-colors duration-300">
|
||||
<header className="absolute top-4 left-4 z-10">
|
||||
<Image src="/app-icon.svg" alt="App Icon" width={40} height={40} />
|
||||
<div className="min-h-screen w-screen overflow-hidden font-[family-name:var(--font-geist-sans)] dark:bg-gray-900 dark:text-white transition-colors duration-300">
|
||||
<header className="absolute top-4 left-4 z-20">
|
||||
<Image src="/app-icon.svg" alt="App Icon" width={32} height={32} />
|
||||
</header>
|
||||
<button
|
||||
className="absolute top-4 right-4 z-10"
|
||||
onClick={toggleHistory}
|
||||
>
|
||||
<Image src={isDarkMode ? "/history-icon-dark.svg" : "/history-icon-light.svg"} alt="History" width={32} height={32} />
|
||||
</button>
|
||||
<main className="flex flex-col items-center justify-center h-full">
|
||||
<div className="absolute top-4 right-4 z-20 flex space-x-2">
|
||||
<button onClick={toggleHistory}>
|
||||
<Image src={isDarkMode ? "/history-icon-dark.svg" : "/history-icon-light.svg"} alt="History" width={24} height={24} />
|
||||
</button>
|
||||
<button onClick={toggleSettings}>
|
||||
<Image src={isDarkMode ? "/settings-icon-dark.svg" : "/settings-icon-light.svg"} alt="Settings" width={24} height={24} />
|
||||
</button>
|
||||
</div>
|
||||
<main className="flex flex-col items-center justify-center h-screen">
|
||||
<AnimatePresence>
|
||||
{schemes.slice(0, 3).map((scheme, index) => (
|
||||
<ColorSchemeCard
|
||||
@ -121,6 +138,8 @@ export default function Home() {
|
||||
onDislike={() => handleDislike(scheme)}
|
||||
index={index}
|
||||
isDarkMode={isDarkMode}
|
||||
codeSample={codeSample}
|
||||
outputFormat={outputFormat}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
@ -131,6 +150,21 @@ export default function Home() {
|
||||
dislikedSchemes={dislikedSchemes}
|
||||
onClose={toggleHistory}
|
||||
isDarkMode={isDarkMode}
|
||||
outputFormat={outputFormat}
|
||||
/>
|
||||
)}
|
||||
{isSettingsOpen && (
|
||||
<Settings
|
||||
isOpen={isSettingsOpen}
|
||||
onClose={toggleSettings}
|
||||
isDarkMode={isDarkMode}
|
||||
onToggleDarkMode={() => setIsDarkMode(!isDarkMode)}
|
||||
outputFormat={outputFormat}
|
||||
setOutputFormat={setOutputFormat}
|
||||
codeSample={codeSample}
|
||||
setCodeSample={setCodeSample}
|
||||
saveSettings={saveSettings}
|
||||
setSaveSettings={setSaveSettings}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,15 +1,16 @@
|
||||
type Color = string; // Hex color code
|
||||
import Color from 'color';
|
||||
|
||||
type ColorScheme = {
|
||||
name: string;
|
||||
colors: {
|
||||
primary: { background: Color; foreground: Color };
|
||||
primary: { background: string; foreground: string };
|
||||
normal: {
|
||||
black: Color; red: Color; green: Color; yellow: Color;
|
||||
blue: Color; magenta: Color; cyan: Color; white: Color;
|
||||
black: string; red: string; green: string; yellow: string;
|
||||
blue: string; magenta: string; cyan: string; white: string;
|
||||
};
|
||||
bright: {
|
||||
black: Color; red: Color; green: Color; yellow: Color;
|
||||
blue: Color; magenta: Color; cyan: Color; white: Color;
|
||||
black: string; red: string; green: string; yellow: string;
|
||||
blue: string; magenta: string; cyan: string; white: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -18,61 +19,108 @@ import knownSchemesData from '../../formatted_themes.json';
|
||||
|
||||
const knownSchemes: ColorScheme[] = knownSchemesData;
|
||||
|
||||
function generateRandomColor(): Color {
|
||||
function generateRandomColor(): string {
|
||||
return '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
|
||||
}
|
||||
|
||||
function generateCreativeName(): string {
|
||||
const adjectives = ['Cosmic', 'Neon', 'Mystic', 'Retro', 'Cyber', 'Ethereal', 'Vibrant', 'Dreamy', 'Futuristic', 'Nostalgic'];
|
||||
const nouns = ['Sunset', 'Aurora', 'Galaxy', 'Ocean', 'Forest', 'Desert', 'Nebula', 'Horizon', 'Oasis', 'Metropolis'];
|
||||
return `${adjectives[Math.floor(Math.random() * adjectives.length)]} ${nouns[Math.floor(Math.random() * nouns.length)]}`;
|
||||
function generateCreativeName(colors: { [key: string]: string }): string {
|
||||
const allColors = Object.values(colors);
|
||||
const dominantColor = getDominantColor(allColors);
|
||||
const mood = getMood(allColors);
|
||||
const theme = getTheme(dominantColor);
|
||||
|
||||
const nameComponents = [mood, theme].filter(Boolean);
|
||||
if (nameComponents.length === 1) {
|
||||
nameComponents.push(getRandomAdjective(dominantColor));
|
||||
}
|
||||
|
||||
return nameComponents.join(' ');
|
||||
}
|
||||
|
||||
function getDominantColor(colors: string[]): Color {
|
||||
return colors.reduce((dominant, color) => {
|
||||
const current = Color(color);
|
||||
return current.luminosity() > dominant.luminosity() ? current : dominant;
|
||||
}, Color(colors[0]));
|
||||
}
|
||||
|
||||
function getMood(colors: string[]): string {
|
||||
const avgSaturation = colors.reduce((sum, color) => sum + Color(color).saturationl(), 0) / colors.length;
|
||||
const avgLightness = colors.reduce((sum, color) => sum + Color(color).lightness(), 0) / colors.length;
|
||||
|
||||
if (avgSaturation > 50 && avgLightness > 50) return 'Vibrant';
|
||||
if (avgSaturation < 30 && avgLightness < 40) return 'Muted';
|
||||
if (avgLightness > 70) return 'Bright';
|
||||
if (avgLightness < 30) return 'Dark';
|
||||
return '';
|
||||
}
|
||||
|
||||
function getTheme(dominantColor: Color): string {
|
||||
const hue = dominantColor.hue();
|
||||
const themes = [
|
||||
{ name: 'Sunset', range: [0, 60] },
|
||||
{ name: 'Citrus', range: [45, 90] },
|
||||
{ name: 'Forest', range: [90, 150] },
|
||||
{ name: 'Ocean', range: [150, 210] },
|
||||
{ name: 'Twilight', range: [210, 270] },
|
||||
{ name: 'Lavender', range: [270, 330] },
|
||||
{ name: 'Berry', range: [330, 360] },
|
||||
];
|
||||
|
||||
const theme = themes.find(t => hue >= t.range[0] && hue < t.range[1]);
|
||||
return theme ? theme.name : '';
|
||||
}
|
||||
|
||||
function getRandomAdjective(color: Color): string {
|
||||
const adjectives = {
|
||||
warm: ['Cozy', 'Toasty', 'Snug'],
|
||||
cool: ['Crisp', 'Fresh', 'Breezy'],
|
||||
neutral: ['Balanced', 'Harmonious', 'Zen'],
|
||||
bright: ['Radiant', 'Luminous', 'Gleaming'],
|
||||
dark: ['Mysterious', 'Enigmatic', 'Shadowy'],
|
||||
};
|
||||
|
||||
const hue = color.hue();
|
||||
const lightness = color.lightness();
|
||||
|
||||
let category: keyof typeof adjectives;
|
||||
if (hue < 60 || hue > 300) category = 'warm';
|
||||
else if (hue >= 60 && hue <= 300) category = 'cool';
|
||||
else if (lightness > 70) category = 'bright';
|
||||
else if (lightness < 30) category = 'dark';
|
||||
else category = 'neutral';
|
||||
|
||||
return adjectives[category][Math.floor(Math.random() * adjectives[category].length)];
|
||||
}
|
||||
|
||||
function generateRandomScheme(): ColorScheme {
|
||||
let x = {
|
||||
name: generateCreativeName() + "rand",
|
||||
colors: {
|
||||
primary: { background: generateRandomColor(), foreground: generateRandomColor() },
|
||||
normal: {
|
||||
black: generateRandomColor(), red: generateRandomColor(), green: generateRandomColor(), yellow: generateRandomColor(),
|
||||
blue: generateRandomColor(), magenta: generateRandomColor(), cyan: generateRandomColor(), white: generateRandomColor()
|
||||
},
|
||||
bright: {
|
||||
black: generateRandomColor(), red: generateRandomColor(), green: generateRandomColor(), yellow: generateRandomColor(),
|
||||
blue: generateRandomColor(), magenta: generateRandomColor(), cyan: generateRandomColor(), white: generateRandomColor()
|
||||
}
|
||||
const colors = {
|
||||
primary: { background: generateRandomColor(), foreground: generateRandomColor() },
|
||||
normal: {
|
||||
black: generateRandomColor(), red: generateRandomColor(), green: generateRandomColor(), yellow: generateRandomColor(),
|
||||
blue: generateRandomColor(), magenta: generateRandomColor(), cyan: generateRandomColor(), white: generateRandomColor()
|
||||
},
|
||||
bright: {
|
||||
black: generateRandomColor(), red: generateRandomColor(), green: generateRandomColor(), yellow: generateRandomColor(),
|
||||
blue: generateRandomColor(), magenta: generateRandomColor(), cyan: generateRandomColor(), white: generateRandomColor()
|
||||
}
|
||||
};
|
||||
|
||||
x.colors.primary.background = x.colors.normal.black;
|
||||
x.colors.primary.foreground = x.colors.bright.white;
|
||||
colors.primary.background = colors.normal.black;
|
||||
colors.primary.foreground = colors.bright.white;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
function crossTitles(title1: string, title2: string): string {
|
||||
const words1 = title1.split(' ');
|
||||
const words2 = title2.split(' ');
|
||||
const firstWord = Math.random() < 0.5 ? words1[0] : words2[1];
|
||||
const secondWord = Math.random() < 0.5 ? words2[0] : words1[1];
|
||||
return `${firstWord} ${secondWord}`;
|
||||
}
|
||||
|
||||
function mutateColor(color: Color): Color {
|
||||
const r = parseInt(color.slice(1, 3), 16);
|
||||
const g = parseInt(color.slice(3, 5), 16);
|
||||
const b = parseInt(color.slice(5, 7), 16);
|
||||
|
||||
const mutateComponent = (component: number) => {
|
||||
const mutation = Math.floor(Math.random() * 51) - 25; // Random number between -25 and 25
|
||||
return Math.max(0, Math.min(255, component + mutation));
|
||||
return {
|
||||
name: generateCreativeName({ ...colors.normal, ...colors.bright }),
|
||||
colors: colors
|
||||
};
|
||||
}
|
||||
|
||||
const newR = mutateComponent(r);
|
||||
const newG = mutateComponent(g);
|
||||
const newB = mutateComponent(b);
|
||||
|
||||
return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`;
|
||||
function mutateColor(color: string): string {
|
||||
const c = Color(color);
|
||||
const hue = (c.hue() + Math.random() * 30 - 15 + 360) % 360;
|
||||
const saturation = Math.max(0, Math.min(100, c.saturationl() + Math.random() * 20 - 10));
|
||||
const lightness = Math.max(0, Math.min(100, c.lightness() + Math.random() * 20 - 10));
|
||||
return c.hsl(hue, saturation, lightness).hex();
|
||||
}
|
||||
|
||||
function generateSchemeFromGeneticAlgorithm(likedSchemes: ColorScheme[], dislikedSchemes: ColorScheme[]): ColorScheme {
|
||||
@ -84,26 +132,26 @@ function generateSchemeFromGeneticAlgorithm(likedSchemes: ColorScheme[], dislike
|
||||
const newScheme: ColorScheme = JSON.parse(JSON.stringify(parentScheme)); // Deep copy
|
||||
|
||||
// Mutate colors
|
||||
Object.keys(newScheme.colors).forEach((colorGroup) => {
|
||||
Object.keys(newScheme.colors[colorGroup]).forEach((colorName) => {
|
||||
Object.keys(newScheme.colors).forEach((colorGroup: keyof typeof newScheme.colors) => {
|
||||
Object.keys(newScheme.colors[colorGroup]).forEach((colorName: string) => {
|
||||
if (Math.random() < 0.3) { // 30% chance of mutation
|
||||
newScheme.colors[colorGroup][colorName] = mutateColor(newScheme.colors[colorGroup][colorName]);
|
||||
(newScheme.colors[colorGroup] as any)[colorName] = mutateColor((newScheme.colors[colorGroup] as any)[colorName]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Avoid similarities with disliked schemes
|
||||
dislikedSchemes.forEach(dislikedScheme => {
|
||||
Object.keys(newScheme.colors).forEach((colorGroup) => {
|
||||
Object.keys(newScheme.colors[colorGroup]).forEach((colorName) => {
|
||||
if (newScheme.colors[colorGroup][colorName] === dislikedScheme.colors[colorGroup][colorName]) {
|
||||
newScheme.colors[colorGroup][colorName] = mutateColor(newScheme.colors[colorGroup][colorName]);
|
||||
Object.keys(newScheme.colors).forEach((colorGroup: keyof typeof newScheme.colors) => {
|
||||
Object.keys(newScheme.colors[colorGroup]).forEach((colorName: string) => {
|
||||
if ((newScheme.colors[colorGroup] as any)[colorName] === (dislikedScheme.colors[colorGroup] as any)[colorName]) {
|
||||
(newScheme.colors[colorGroup] as any)[colorName] = mutateColor((newScheme.colors[colorGroup] as any)[colorName]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
newScheme.name = generateCreativeName() + "gen";
|
||||
newScheme.name = generateCreativeName({ ...newScheme.colors.normal, ...newScheme.colors.bright });
|
||||
return newScheme;
|
||||
}
|
||||
|
||||
|
87
app/utils/exportFormats.ts
Normal file
87
app/utils/exportFormats.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { ColorScheme } from './colorSchemes';
|
||||
|
||||
export function generateYAML(scheme: ColorScheme): string {
|
||||
return `colors:
|
||||
# Default colors
|
||||
primary:
|
||||
background: '${scheme.colors.primary.background}'
|
||||
foreground: '${scheme.colors.primary.foreground}'
|
||||
|
||||
# Normal colors
|
||||
normal:
|
||||
black: '${scheme.colors.normal.black}'
|
||||
red: '${scheme.colors.normal.red}'
|
||||
green: '${scheme.colors.normal.green}'
|
||||
yellow: '${scheme.colors.normal.yellow}'
|
||||
blue: '${scheme.colors.normal.blue}'
|
||||
magenta: '${scheme.colors.normal.magenta}'
|
||||
cyan: '${scheme.colors.normal.cyan}'
|
||||
white: '${scheme.colors.normal.white}'
|
||||
|
||||
# Bright colors
|
||||
bright:
|
||||
black: '${scheme.colors.bright.black}'
|
||||
red: '${scheme.colors.bright.red}'
|
||||
green: '${scheme.colors.bright.green}'
|
||||
yellow: '${scheme.colors.bright.yellow}'
|
||||
blue: '${scheme.colors.bright.blue}'
|
||||
magenta: '${scheme.colors.bright.magenta}'
|
||||
cyan: '${scheme.colors.bright.cyan}'
|
||||
white: '${scheme.colors.bright.white}'
|
||||
`;
|
||||
}
|
||||
|
||||
export function generateJSON(scheme: ColorScheme): string {
|
||||
return JSON.stringify(scheme.colors, null, 2);
|
||||
}
|
||||
|
||||
export function generateXResources(scheme: ColorScheme): string {
|
||||
return `*background: ${scheme.colors.primary.background}
|
||||
*foreground: ${scheme.colors.primary.foreground}
|
||||
|
||||
*color0: ${scheme.colors.normal.black}
|
||||
*color1: ${scheme.colors.normal.red}
|
||||
*color2: ${scheme.colors.normal.green}
|
||||
*color3: ${scheme.colors.normal.yellow}
|
||||
*color4: ${scheme.colors.normal.blue}
|
||||
*color5: ${scheme.colors.normal.magenta}
|
||||
*color6: ${scheme.colors.normal.cyan}
|
||||
*color7: ${scheme.colors.normal.white}
|
||||
|
||||
*color8: ${scheme.colors.bright.black}
|
||||
*color9: ${scheme.colors.bright.red}
|
||||
*color10: ${scheme.colors.bright.green}
|
||||
*color11: ${scheme.colors.bright.yellow}
|
||||
*color12: ${scheme.colors.bright.blue}
|
||||
*color13: ${scheme.colors.bright.magenta}
|
||||
*color14: ${scheme.colors.bright.cyan}
|
||||
*color15: ${scheme.colors.bright.white}
|
||||
`;
|
||||
}
|
||||
|
||||
export function generateTOML(scheme: ColorScheme): string {
|
||||
return `[colors.primary]
|
||||
background = "${scheme.colors.primary.background}"
|
||||
foreground = "${scheme.colors.primary.foreground}"
|
||||
|
||||
[colors.normal]
|
||||
black = "${scheme.colors.normal.black}"
|
||||
red = "${scheme.colors.normal.red}"
|
||||
green = "${scheme.colors.normal.green}"
|
||||
yellow = "${scheme.colors.normal.yellow}"
|
||||
blue = "${scheme.colors.normal.blue}"
|
||||
magenta = "${scheme.colors.normal.magenta}"
|
||||
cyan = "${scheme.colors.normal.cyan}"
|
||||
white = "${scheme.colors.normal.white}"
|
||||
|
||||
[colors.bright]
|
||||
black = "${scheme.colors.bright.black}"
|
||||
red = "${scheme.colors.bright.red}"
|
||||
green = "${scheme.colors.bright.green}"
|
||||
yellow = "${scheme.colors.bright.yellow}"
|
||||
blue = "${scheme.colors.bright.blue}"
|
||||
magenta = "${scheme.colors.bright.magenta}"
|
||||
cyan = "${scheme.colors.bright.cyan}"
|
||||
white = "${scheme.colors.bright.white}"
|
||||
`;
|
||||
}
|
Reference in New Issue
Block a user