Add share button and share view. and cleanup
This commit is contained in:
parent
ce7b1e6679
commit
12a697decd
295
app/components/CodePreview.tsx
Normal file
295
app/components/CodePreview.tsx
Normal 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;
|
@ -1,12 +1,12 @@
|
||||
import React, { useState } from 'react';
|
||||
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 { Highlight, themes } from 'prism-react-renderer';
|
||||
import { motion, useAnimation } from 'framer-motion';
|
||||
import ColorPalette from './ColorPalette';
|
||||
import confetti from 'canvas-confetti';
|
||||
import { AppSettings } from '../utils/types';
|
||||
import CodePreview from './CodePreview';
|
||||
|
||||
interface ColorSchemeCardProps {
|
||||
scheme: ColorScheme;
|
||||
@ -21,234 +21,6 @@ const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, onLike, onDis
|
||||
const [overlayColor, setOverlayColor] = useState('rgba(0, 0, 0, 0)');
|
||||
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) => {
|
||||
e.stopPropagation();
|
||||
let content: string;
|
||||
@ -315,6 +87,12 @@ fi`
|
||||
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 = [
|
||||
scheme.colors.normal.black,
|
||||
scheme.colors.normal.red,
|
||||
@ -358,78 +136,23 @@ fi`
|
||||
/>
|
||||
<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={20} height={20} />
|
||||
</button>
|
||||
<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
|
||||
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={20} height={20} />
|
||||
</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>
|
||||
))}
|
||||
</pre>
|
||||
)}
|
||||
</Highlight>
|
||||
<CodePreview scheme={scheme} codeSample={settings.codeSample} />
|
||||
</div>
|
||||
<div className="mt-2 z-10">
|
||||
<ColorPalette
|
||||
@ -437,18 +160,18 @@ fi`
|
||||
size="small"
|
||||
/>
|
||||
</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
|
||||
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}
|
||||
>
|
||||
<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
|
||||
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}
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,7 +7,6 @@ interface SettingsProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
isDarkMode: boolean;
|
||||
onToggleDarkMode: () => void;
|
||||
settings: AppSettings;
|
||||
setSettings: (settings: AppSettings) => void;
|
||||
saveSettings: boolean;
|
||||
@ -18,7 +17,6 @@ const Settings: React.FC<SettingsProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
isDarkMode,
|
||||
onToggleDarkMode,
|
||||
settings,
|
||||
setSettings,
|
||||
saveSettings,
|
||||
@ -58,21 +56,6 @@ const Settings: React.FC<SettingsProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<div className="flex items-center">
|
||||
|
@ -176,7 +176,6 @@ export default function Home() {
|
||||
isOpen={isSettingsOpen}
|
||||
onClose={toggleSettings}
|
||||
isDarkMode={isDarkMode}
|
||||
onToggleDarkMode={() => setIsDarkMode(!isDarkMode)}
|
||||
settings={settings}
|
||||
setSettings={setSettings}
|
||||
saveSettings={saveSettings}
|
||||
|
143
app/share/[id]/page.tsx
Normal file
143
app/share/[id]/page.tsx
Normal 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;
|
@ -166,28 +166,6 @@ function getTheme(dominantColor: Color): string {
|
||||
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 {
|
||||
if (totalSchemes < 30) {
|
||||
return generateCompletelyRandomScheme();
|
||||
@ -351,5 +329,49 @@ function generateNoun(): string {
|
||||
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 { knownSchemes, generateRandomScheme, generateSchemeFromGeneticAlgorithm };
|
7
public/share-icon-dark.svg
Normal file
7
public/share-icon-dark.svg
Normal 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 |
7
public/share-icon-light.svg
Normal file
7
public/share-icon-light.svg
Normal 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 |
Loading…
Reference in New Issue
Block a user