From 12a697decd4da86cc8a64956905cd7c1a4810958 Mon Sep 17 00:00:00 2001 From: Tanishq Dubey Date: Tue, 10 Sep 2024 14:08:02 -0400 Subject: [PATCH] Add share button and share view. and cleanup --- app/components/CodePreview.tsx | 295 +++++++++++++++++++++++++ app/components/ColorSchemeCard.tsx | 333 +++-------------------------- app/components/Settings.tsx | 17 -- app/page.tsx | 1 - app/share/[id]/page.tsx | 143 +++++++++++++ app/utils/colorSchemes.ts | 66 ++++-- public/share-icon-dark.svg | 7 + public/share-icon-light.svg | 7 + 8 files changed, 524 insertions(+), 345 deletions(-) create mode 100644 app/components/CodePreview.tsx create mode 100644 app/share/[id]/page.tsx create mode 100644 public/share-icon-dark.svg create mode 100644 public/share-icon-light.svg diff --git a/app/components/CodePreview.tsx b/app/components/CodePreview.tsx new file mode 100644 index 0000000..51a86f5 --- /dev/null +++ b/app/components/CodePreview.tsx @@ -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 = ({ scheme, codeSample }) => { + const getCodeExample = () => { + const samples = { + c: `#include + + 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 { + 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 ( +
+ + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+            {tokens.map((line, 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., ) + 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 ; + })} +
+ ))} +
+ )} +
+
+ ); +}; + +export default CodePreview; \ No newline at end of file diff --git a/app/components/ColorSchemeCard.tsx b/app/components/ColorSchemeCard.tsx index 1a5e966..94f33b4 100644 --- a/app/components/ColorSchemeCard.tsx +++ b/app/components/ColorSchemeCard.tsx @@ -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 = ({ 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 - -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 { - 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` />

{scheme.name}

- +
+ + +
- - {({ className, style, tokens, getLineProps, getTokenProps }) => ( -
-                {tokens.map((line, 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., ) - 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 ; - })} -
- ))} -
- )} -
+
-
+
diff --git a/app/components/Settings.tsx b/app/components/Settings.tsx index 0948069..06131d4 100644 --- a/app/components/Settings.tsx +++ b/app/components/Settings.tsx @@ -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 = ({ isOpen, onClose, isDarkMode, - onToggleDarkMode, settings, setSettings, saveSettings, @@ -58,21 +56,6 @@ const Settings: React.FC = ({
-
- Dark Mode - -
diff --git a/app/page.tsx b/app/page.tsx index 0a56e0f..7648a7e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -176,7 +176,6 @@ export default function Home() { isOpen={isSettingsOpen} onClose={toggleSettings} isDarkMode={isDarkMode} - onToggleDarkMode={() => setIsDarkMode(!isDarkMode)} settings={settings} setSettings={setSettings} saveSettings={saveSettings} diff --git a/app/share/[id]/page.tsx b/app/share/[id]/page.tsx new file mode 100644 index 0000000..16d36b4 --- /dev/null +++ b/app/share/[id]/page.tsx @@ -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(null); + const [codeSample, setCodeSample] = useState('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
Loading...
; + } + + 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 ( +
+
+
+ App Icon +
+

TerminalTinder

+

Fall in love with your next color scheme

+
+
+
+
+
+
+

{scheme.name}

+
+

Color Palette

+ +
+
+

Code Preview

+ +
+
+

Download

+
+ + +
+
+
+

Share

+ +
+
+
+
+
+ ); +}; + +export default SharedTheme; \ No newline at end of file diff --git a/app/utils/colorSchemes.ts b/app/utils/colorSchemes.ts index df16550..b496c8b 100644 --- a/app/utils/colorSchemes.ts +++ b/app/utils/colorSchemes.ts @@ -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>, group) => { + acc[group] = colorNames[group as keyof typeof colorNames].reduce((groupAcc: Record, 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 }; \ No newline at end of file diff --git a/public/share-icon-dark.svg b/public/share-icon-dark.svg new file mode 100644 index 0000000..3f809e6 --- /dev/null +++ b/public/share-icon-dark.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/public/share-icon-light.svg b/public/share-icon-light.svg new file mode 100644 index 0000000..0a37392 --- /dev/null +++ b/public/share-icon-light.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file