Add share button and share view. and cleanup

This commit is contained in:
Tanishq Dubey 2024-09-10 14:08:02 -04:00
parent ce7b1e6679
commit 12a697decd
8 changed files with 524 additions and 345 deletions

View File

@ -0,0 +1,295 @@
import React from 'react';
import { Highlight, themes } from 'prism-react-renderer';
import { ColorScheme } from '../utils/colorSchemes';
import { CodeSample } from '../utils/types';
interface CodePreviewProps {
scheme: ColorScheme;
codeSample: CodeSample;
}
const CodePreview: React.FC<CodePreviewProps> = ({ scheme, codeSample }) => {
const getCodeExample = () => {
const samples = {
c: `#include <stdio.h>
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;
}
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");
}
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));
}`,
java: `public class PrimeNumbers {
public static boolean isPrime(int n) {
if (n <= 1) return false;
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) return false;
}
return true;
}
public static void primesUpTo(int limit) {
System.out.println("Primes up to " + limit + ":");
for (int i = 2; i <= limit; i++) {
if (isPrime(i)) {
System.out.print(i + " ");
}
}
System.out.println();
}
`,
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: `/* Calculate the area and circumference of a circle */
const pi = 3.14;
function calculateArea(r) {
return pi * r ** 2; // Exponentiation, constant, operator
}
function calculateCircumference(r) {
return 2 * pi * r; // Function, return, operators
}
if (radius > 0) {
console.log(\`Area: $\{calculateArea(radius)\}\`); // Template string, method
console.log(\`Circumference: $\{calculateCircumference(radius)\}\`);
} else {
console.error("Invalid radius!"); // Error handling
}
try {
radius = -1;
if (radius < 0) throw new Error("Negative radius"); // Throw, error
} catch (e) {
console.warn(e.message); // Catch block, method
}`,
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;
};
return (
<div className="bg-gray-100 dark:bg-gray-700 rounded-md overflow-x-auto">
<Highlight theme={themes.dracula} code={getCodeExample()} language={codeSample}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={`${className} text-sm`} 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;
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>
))}
</pre>
)}
</Highlight>
</div>
);
};
export default CodePreview;

View File

@ -1,12 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import { ColorScheme } from '../utils/colorSchemes'; import { ColorScheme, encodeThemeForUrl } from '../utils/colorSchemes';
import { generateYAML, generateJSON, generateXResources, generateTOML, generateITerm2, generateWindowsTerminal, generateTerminalApp } from '../utils/exportFormats'; import { generateYAML, generateJSON, generateXResources, generateTOML, generateITerm2, generateWindowsTerminal, generateTerminalApp } from '../utils/exportFormats';
import { Highlight, themes } from 'prism-react-renderer';
import { motion, useAnimation } from 'framer-motion'; import { motion, useAnimation } from 'framer-motion';
import ColorPalette from './ColorPalette'; import ColorPalette from './ColorPalette';
import confetti from 'canvas-confetti'; import confetti from 'canvas-confetti';
import { AppSettings } from '../utils/types'; import { AppSettings } from '../utils/types';
import CodePreview from './CodePreview';
interface ColorSchemeCardProps { interface ColorSchemeCardProps {
scheme: ColorScheme; scheme: ColorScheme;
@ -21,234 +21,6 @@ const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, onLike, onDis
const [overlayColor, setOverlayColor] = useState('rgba(0, 0, 0, 0)'); const [overlayColor, setOverlayColor] = useState('rgba(0, 0, 0, 0)');
const controls = useAnimation(); const controls = useAnimation();
const getCodeExample = () => {
if (settings.juniorDevMode) {
return `
// This code is perfect, no need to review
function makeItWork() {
return true;
}
// This function does everything
function doEverything() {
// TODO: Implement
}
// Ignore this, it's probably not important
try {
somethingRisky();
} catch (e) {
// This will never happen
}
// Copy-pasted from StackOverflow
const regex = /^([a-zA-Z0-9_\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9]{2,4})+$/;
// I'll fix this later
while (true) {
// Infinite loop for extra performance
}
`;
}
const samples = {
c: `#include <stdio.h>
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;
}
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");
}
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: `/* Calculate the area and circumference of a circle */
const pi = 3.14;
function calculateArea(r) {
return pi * r ** 2; // Exponentiation, constant, operator
}
function calculateCircumference(r) {
return 2 * pi * r; // Function, return, operators
}
if (radius > 0) {
console.log(\`Area: $\{calculateArea(radius)\}\`); // Template string, method
console.log(\`Circumference: $\{calculateCircumference(radius)\}\`);
} else {
console.error("Invalid radius!"); // Error handling
}
try {
radius = -1;
if (radius < 0) throw new Error("Negative radius"); // Throw, error
} catch (e) {
console.warn(e.message); // Catch block, method
}`,
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`
};
type SampleLanguage = keyof typeof samples;
// Type guard to check if codeSample is a valid key of samples
const isValidSample = (sample: string): sample is SampleLanguage => {
return sample in samples;
};
return isValidSample(settings.codeSample) ? samples[settings.codeSample] : samples.javascript;
};
const handleDownload = (e: React.MouseEvent) => { const handleDownload = (e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
let content: string; let content: string;
@ -315,6 +87,12 @@ fi`
controls.start({ x: -300, opacity: 0, transition: { duration: 0.3 } }).then(onDislike); controls.start({ x: -300, opacity: 0, transition: { duration: 0.3 } }).then(onDislike);
}; };
const handleShare = (e: React.MouseEvent) => {
e.stopPropagation();
const encodedTheme = encodeThemeForUrl(scheme);
window.open(`/share/${encodedTheme}`, '_blank');
};
const paletteColors = [ const paletteColors = [
scheme.colors.normal.black, scheme.colors.normal.black,
scheme.colors.normal.red, scheme.colors.normal.red,
@ -358,6 +136,13 @@ fi`
/> />
<div className="flex justify-between items-center mb-2 z-10"> <div className="flex justify-between items-center mb-2 z-10">
<h2 className="text-lg font-semibold truncate">{scheme.name}</h2> <h2 className="text-lg font-semibold truncate">{scheme.name}</h2>
<div className="flex space-x-2">
<button
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors duration-300"
onClick={handleShare}
>
<Image src={isDarkMode ? "/share-icon-dark.svg" : "/share-icon-light.svg"} alt="Share" width={20} height={20} />
</button>
<button <button
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors duration-300" className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors duration-300"
onClick={handleDownload} onClick={handleDownload}
@ -365,71 +150,9 @@ fi`
<Image src={isDarkMode ? "/download-icon-dark.svg" : "/download-icon-light.svg"} alt="Download" width={20} height={20} /> <Image src={isDarkMode ? "/download-icon-dark.svg" : "/download-icon-light.svg"} alt="Download" width={20} height={20} />
</button> </button>
</div> </div>
<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={settings.codeSample}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<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;
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> </div>
))} <div className="bg-gray-100 dark:bg-gray-700 rounded-md mb-2 flex-grow overflow-hidden z-10 shadow-md">
</pre> <CodePreview scheme={scheme} codeSample={settings.codeSample} />
)}
</Highlight>
</div> </div>
<div className="mt-2 z-10"> <div className="mt-2 z-10">
<ColorPalette <ColorPalette
@ -437,18 +160,18 @@ fi`
size="small" size="small"
/> />
</div> </div>
<div className="flex justify-center space-x-4 mt-2 z-10"> <div className="flex justify-center space-x-6 mt-4 z-10">
<button <button
className="bg-red-500 text-white p-2 rounded-full shadow-lg hover:bg-red-600 transition-colors duration-300" className="bg-red-500 text-white p-3 rounded-full shadow-lg hover:bg-red-600 transition-colors duration-300"
onClick={handleDislike} onClick={handleDislike}
> >
<Image src={isDarkMode ? "/cross-icon-dark.svg" : "/cross-icon-light.svg"} alt="Dislike" width={24} height={24} /> <Image src={isDarkMode ? "/cross-icon-dark.svg" : "/cross-icon-light.svg"} alt="Dislike" width={32} height={32} />
</button> </button>
<button <button
className="bg-green-500 text-white p-2 rounded-full shadow-lg hover:bg-green-600 transition-colors duration-300" className="bg-green-500 text-white p-3 rounded-full shadow-lg hover:bg-green-600 transition-colors duration-300"
onClick={handleLike} onClick={handleLike}
> >
<Image src={isDarkMode ? "/heart-icon-dark.svg" : "/heart-icon-light.svg"} alt="Like" width={24} height={24} /> <Image src={isDarkMode ? "/heart-icon-dark.svg" : "/heart-icon-light.svg"} alt="Like" width={32} height={32} />
</button> </button>
</div> </div>
</div> </div>

View File

@ -7,7 +7,6 @@ interface SettingsProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
isDarkMode: boolean; isDarkMode: boolean;
onToggleDarkMode: () => void;
settings: AppSettings; settings: AppSettings;
setSettings: (settings: AppSettings) => void; setSettings: (settings: AppSettings) => void;
saveSettings: boolean; saveSettings: boolean;
@ -18,7 +17,6 @@ const Settings: React.FC<SettingsProps> = ({
isOpen, isOpen,
onClose, onClose,
isDarkMode, isDarkMode,
onToggleDarkMode,
settings, settings,
setSettings, setSettings,
saveSettings, saveSettings,
@ -58,21 +56,6 @@ const Settings: React.FC<SettingsProps> = ({
</button> </button>
</div> </div>
<div className="space-y-4"> <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> <div>
<label className="block mb-2">Output Format</label> <label className="block mb-2">Output Format</label>
<div className="flex items-center"> <div className="flex items-center">

View File

@ -176,7 +176,6 @@ export default function Home() {
isOpen={isSettingsOpen} isOpen={isSettingsOpen}
onClose={toggleSettings} onClose={toggleSettings}
isDarkMode={isDarkMode} isDarkMode={isDarkMode}
onToggleDarkMode={() => setIsDarkMode(!isDarkMode)}
settings={settings} settings={settings}
setSettings={setSettings} setSettings={setSettings}
saveSettings={saveSettings} saveSettings={saveSettings}

143
app/share/[id]/page.tsx Normal file
View File

@ -0,0 +1,143 @@
'use client';
import React, { useEffect, useState } from 'react';
import { useParams } from 'next/navigation';
import Image from 'next/image';
import { ColorScheme, decodeThemeFromUrl } from '../../utils/colorSchemes';
import ColorPalette from '../../components/ColorPalette';
import CodePreview from '../../components/CodePreview';
import { CodeSample } from '../../utils/types';
import { generateYAML, generateJSON, generateXResources, generateTOML, generateITerm2, generateWindowsTerminal, generateTerminalApp } from '../../utils/exportFormats';
const SharedTheme: React.FC = () => {
const params = useParams();
const [scheme, setScheme] = useState<ColorScheme | null>(null);
const [codeSample, setCodeSample] = useState<CodeSample>('javascript');
const [outputFormat, setOutputFormat] = useState('yaml');
const [isDarkMode, setIsDarkMode] = useState(false);
useEffect(() => {
if (params.id) {
const decodedScheme = decodeThemeFromUrl(params.id as string);
setScheme(decodedScheme);
}
setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches);
}, [params.id]);
if (!scheme) {
return <div>Loading...</div>;
}
const handleDownload = () => {
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 'iterm2':
content = generateITerm2(scheme);
fileExtension = 'itermcolors';
break;
case 'windows-terminal':
content = generateWindowsTerminal(scheme);
fileExtension = 'json';
break;
case 'terminal-app':
content = generateTerminalApp(scheme);
fileExtension = 'terminal';
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()}.${fileExtension}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
return (
<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-2 left-2 right-2 flex justify-between items-start z-20">
<div className="flex items-center">
<Image src="/app-icon.svg" alt="App Icon" width={32} height={32} className="mr-2" />
<div>
<h1 className="text-lg font-bold">TerminalTinder</h1>
<p className="text-xs">Fall in love with your next color scheme</p>
</div>
</div>
</header>
<main className="pt-16 px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto bg-white dark:bg-gray-800 shadow-xl rounded-lg overflow-hidden">
<div className="p-8">
<h1 className="text-3xl font-bold mb-4">{scheme.name}</h1>
<div className="mb-6">
<h2 className="text-xl font-semibold mb-2">Color Palette</h2>
<ColorPalette
colors={Object.values(scheme.colors.normal).concat(Object.values(scheme.colors.bright))}
size="large"
/>
</div>
<div className="mb-6">
<h2 className="text-xl font-semibold mb-2">Code Preview</h2>
<CodePreview scheme={scheme} codeSample={codeSample} />
</div>
<div className="mb-6">
<h2 className="text-xl font-semibold mb-2">Download</h2>
<div className="flex items-center space-x-4">
<select
value={outputFormat}
onChange={(e) => setOutputFormat(e.target.value)}
className="p-2 border rounded dark:bg-gray-700 dark:border-gray-600"
>
<option value="yaml">YAML (Alacritty)</option>
<option value="json">JSON</option>
<option value="xresources">XResources</option>
<option value="toml">TOML (Alacritty)</option>
<option value="iterm2">iTerm2</option>
<option value="windows-terminal">Windows Terminal</option>
<option value="terminal-app">Terminal.app</option>
</select>
<button
onClick={handleDownload}
className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition-colors duration-300"
>
Download
</button>
</div>
</div>
<div>
<h2 className="text-xl font-semibold mb-2">Share</h2>
<input
type="text"
value={window.location.href}
readOnly
className="w-full p-2 border rounded bg-gray-100 dark:bg-gray-700 dark:border-gray-600"
/>
</div>
</div>
</div>
</main>
</div>
);
};
export default SharedTheme;

View File

@ -166,28 +166,6 @@ function getTheme(dominantColor: Color): string {
return theme ? theme.name : ''; 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(totalSchemes: number): ColorScheme { function generateRandomScheme(totalSchemes: number): ColorScheme {
if (totalSchemes < 30) { if (totalSchemes < 30) {
return generateCompletelyRandomScheme(); return generateCompletelyRandomScheme();
@ -351,5 +329,49 @@ function generateNoun(): string {
return nouns[Math.floor(Math.random() * nouns.length)]; return nouns[Math.floor(Math.random() * nouns.length)];
} }
import { Buffer } from 'buffer';
export function encodeThemeForUrl(scheme: ColorScheme): string {
const encodedColors = Object.entries(scheme.colors).flatMap(([group, colors]) =>
Object.entries(colors).map(([name, color]) => color.slice(1))
).join('');
const data = JSON.stringify({
name: scheme.name,
colors: encodedColors
});
return Buffer.from(data).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
export function decodeThemeFromUrl(encodedTheme: string): ColorScheme {
const base64 = encodedTheme.replace(/-/g, '+').replace(/_/g, '/');
const paddedBase64 = base64 + '='.repeat((4 - base64.length % 4) % 4);
const decodedString = Buffer.from(paddedBase64, 'base64').toString('utf-8');
const decoded = JSON.parse(decodedString);
const colors = decoded.colors.match(/.{6}/g);
const colorGroups = ['primary', 'normal', 'bright'];
const colorNames = {
primary: ['background', 'foreground'],
normal: ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'],
bright: ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']
};
let colorIndex = 0;
const decodedColors = colorGroups.reduce((acc: Record<string, Record<string, string>>, group) => {
acc[group] = colorNames[group as keyof typeof colorNames].reduce((groupAcc: Record<string, string>, name: string) => {
groupAcc[name] = `#${colors[colorIndex++]}`;
return groupAcc;
}, {});
return acc;
}, {});
return {
name: decoded.name,
colors: decodedColors as ColorScheme['colors']
};
}
export type { ColorScheme }; export type { ColorScheme };
export { knownSchemes, generateRandomScheme, generateSchemeFromGeneticAlgorithm }; export { knownSchemes, generateRandomScheme, generateSchemeFromGeneticAlgorithm };

View File

@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M20 4L12 12M20 4V8.5M20 4H15.5M19 12.5V16.8C19 17.9201 19 18.4802 18.782 18.908C18.5903 19.2843 18.2843 19.5903 17.908 19.782C17.4802 20 16.9201 20 15.8 20H7.2C6.0799 20 5.51984 20 5.09202 19.782C4.71569 19.5903 4.40973 19.2843 4.21799 18.908C4 18.4802 4 17.9201 4 16.8V8.2C4 7.0799 4 6.51984 4.21799 6.09202C4.40973 5.71569 4.71569 5.40973 5.09202 5.21799C5.51984 5 6.07989 5 7.2 5H11.5" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g>
</svg>

After

Width:  |  Height:  |  Size: 936 B

View File

@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M20 4L12 12M20 4V8.5M20 4H15.5M19 12.5V16.8C19 17.9201 19 18.4802 18.782 18.908C18.5903 19.2843 18.2843 19.5903 17.908 19.782C17.4802 20 16.9201 20 15.8 20H7.2C6.0799 20 5.51984 20 5.09202 19.782C4.71569 19.5903 4.40973 19.2843 4.21799 18.908C4 18.4802 4 17.9201 4 16.8V8.2C4 7.0799 4 6.51984 4.21799 6.09202C4.40973 5.71569 4.71569 5.40973 5.09202 5.21799C5.51984 5 6.07989 5 7.2 5H11.5" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g>
</svg>

After

Width:  |  Height:  |  Size: 936 B