MVP Complete

This commit is contained in:
Tanishq Dubey 2024-09-09 11:51:54 -04:00
parent 0f56648a93
commit 70b3d7327a
13 changed files with 10086 additions and 98 deletions

View File

@ -0,0 +1,19 @@
import React from 'react';
interface ActionButtonProps {
onClick: () => void;
label: string;
}
const ActionButton: React.FC<ActionButtonProps> = ({ onClick, label }) => {
return (
<button
className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition-colors duration-300"
onClick={onClick}
>
{label}
</button>
);
};
export default ActionButton;

View File

@ -0,0 +1,116 @@
import React from 'react';
import Image from 'next/image';
import { ColorScheme } from '../utils/colorSchemes';
import { generateYAML } from '../utils/yamlExport';
import { Highlight, themes } from 'prism-react-renderer';
interface ColorSchemeCardProps {
scheme: ColorScheme;
isSelected: boolean;
onSelect: () => void;
}
const ColorSchemeCard: React.FC<ColorSchemeCardProps> = ({ scheme, isSelected, onSelect }) => {
const codeExample = `
// User object and function
const user = {
name: 'DWS',
power: 8999
};
class Shape {
constructor(color) {
this.color = color;
}
}
// Async data fetch simulation
async function fetchData() {
return await new Promise(resolve => setTimeout(() => resolve('Data loaded'), 500));
}
const [even, odd] = [2, 4, 6, 8].reduce(([e, o], n) => n % 2 ? [e, [...o, n]] : [
[...e, n], o
], [
[],
[]
]);
/*
Logging here
*/
fetchData().then(data => console.log(data)); `;
const handleDownload = (e: React.MouseEvent) => {
e.stopPropagation();
const yaml = generateYAML(scheme);
const blob = new Blob([yaml], { type: 'text/yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${scheme.name.replace(/\s+/g, '_').toLowerCase()}.yaml`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
return (
<div
className={`w-96 bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden hover:shadow-xl transition-all duration-300 ease-in-out cursor-pointer ${isSelected ? 'ring-4 ring-blue-500' : ''}`}
onClick={onSelect}
>
<div className="p-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">{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="/download-icon.svg" alt="Download" width={24} height={24} />
</button>
</div>
<div className="bg-gray-100 dark:bg-gray-700 p-4 rounded-md mb-4 transition-colors duration-300">
<Highlight theme={themes.dracula} code={codeExample} language="javascript">
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={`${className} text-sm overflow-x-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;
return <span key={key} {...getTokenProps({ token, key })} style={{ ...getTokenProps({ token, key }).style, color }} />;
})}
</div>
))}
</pre>
)}
</Highlight>
</div>
<div className="grid grid-cols-8 gap-2">
{Object.values(scheme.colors.normal).concat(Object.values(scheme.colors.bright)).map((color, index) => (
<div
key={index}
className="w-full pt-full rounded-sm transition-colors duration-300 relative group"
style={{backgroundColor: color}}
>
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-black bg-opacity-50 text-white text-xs">
{color}
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default ColorSchemeCard;

View File

@ -0,0 +1,43 @@
import React from 'react';
interface SettingsProps {
isDarkMode: boolean;
onToggleDarkMode: () => void;
isOpen: boolean;
onClose: () => void;
}
const Settings: React.FC<SettingsProps> = ({ isDarkMode, onToggleDarkMode, isOpen, onClose }) => {
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl">
<h2 className="text-xl font-bold mb-4">Settings</h2>
<div className="flex items-center justify-between mb-4">
<span>Dark Mode</span>
<button
onClick={onToggleDarkMode}
className={`w-12 h-6 rounded-full p-1 transition-colors duration-300 ease-in-out ${
isDarkMode ? 'bg-blue-600' : 'bg-gray-300'
}`}
>
<div
className={`w-4 h-4 rounded-full bg-white transform transition-transform duration-300 ease-in-out ${
isDarkMode ? 'translate-x-6' : ''
}`}
/>
</button>
</div>
<button
onClick={onClose}
className="mt-4 bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded transition-colors duration-300"
>
Close
</button>
</div>
</div>
);
};
export default Settings;

View File

@ -0,0 +1,16 @@
import React from 'react';
import Image from 'next/image';
interface SettingsIconProps {
onClick: () => void;
}
const SettingsIcon: React.FC<SettingsIconProps> = ({ onClick }) => {
return (
<button className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200" onClick={onClick}>
<Image src="/settings-icon.svg" alt="Settings" width={24} height={24} />
</button>
);
};
export default SettingsIcon;

View File

@ -17,7 +17,7 @@
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
font-family: var(--font-geist-sans), Arial, sans-serif;
}
@layer utilities {
@ -25,3 +25,7 @@ body {
text-wrap: balance;
}
}
.pt-full {
padding-top: 100%;
}

View File

@ -1,101 +1,110 @@
import Image from "next/image";
'use client';
import React, { useState, useEffect } from 'react';
import ColorSchemeCard from "./components/ColorSchemeCard";
import ActionButton from "./components/ActionButton";
import SettingsIcon from "./components/SettingsIcon";
import Settings from "./components/Settings";
import { ColorScheme, knownSchemes, generateRandomScheme, crossSchemes, generateSchemeFromGeneticAlgorithm } from './utils/colorSchemes';
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="https://nextjs.org/icons/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
const [schemes, setSchemes] = useState<ColorScheme[]>([]);
const [selectedSchemes, setSelectedSchemes] = useState<number[]>([]);
const [isDarkMode, setIsDarkMode] = useState(false);
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
const [likedSchemes, setLikedSchemes] = useState<ColorScheme[]>([]);
const [dislikedSchemes, setDislikedSchemes] = useState<ColorScheme[]>([]);
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="https://nextjs.org/icons/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
useEffect(() => {
generateNewSchemes();
setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches);
const storedLikedSchemes = localStorage.getItem('likedSchemes');
const storedDislikedSchemes = localStorage.getItem('dislikedSchemes');
if (storedLikedSchemes) {
setLikedSchemes(JSON.parse(storedLikedSchemes));
}
if (storedDislikedSchemes) {
setDislikedSchemes(JSON.parse(storedDislikedSchemes));
}
}, []);
useEffect(() => {
document.documentElement.classList.toggle('dark', isDarkMode);
}, [isDarkMode]);
useEffect(() => {
localStorage.setItem('likedSchemes', JSON.stringify(likedSchemes));
}, [likedSchemes]);
useEffect(() => {
localStorage.setItem('dislikedSchemes', JSON.stringify(dislikedSchemes));
}, [dislikedSchemes]);
const generateNewSchemes = () => {
const newSchemes = [
knownSchemes[Math.floor(Math.random() * knownSchemes.length)],
generateRandomScheme(),
likedSchemes.length > 0 ? generateSchemeFromGeneticAlgorithm(likedSchemes, dislikedSchemes) : generateRandomScheme()
];
setSchemes(newSchemes);
setSelectedSchemes([]);
};
const handleSchemeSelect = (index: number) => {
setSelectedSchemes(prev => {
if (prev.includes(index)) {
return prev.filter(i => i !== index);
} else if (prev.length < 2) {
return [...prev, index];
}
return prev;
});
};
const handleAction = () => {
if (selectedSchemes.length === 2) {
const crossedScheme = crossSchemes(schemes[selectedSchemes[0]], schemes[selectedSchemes[1]]);
setLikedSchemes(prev => [...prev, schemes[selectedSchemes[0]], schemes[selectedSchemes[1]]]);
setSchemes([crossedScheme, knownSchemes[Math.floor(Math.random() * knownSchemes.length)], generateSchemeFromGeneticAlgorithm(likedSchemes, dislikedSchemes)]);
setSelectedSchemes([]);
} else {
setDislikedSchemes(prev => [...prev, ...schemes]);
generateNewSchemes();
}
};
const toggleDarkMode = () => {
setIsDarkMode(prev => !prev);
};
return (
<div className="min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)] dark:bg-gray-900 dark:text-white transition-colors duration-300">
<header className="flex justify-between items-center mb-8">
<h1 className="text-2xl font-bold">Terminal Color Scheme Generator</h1>
<SettingsIcon onClick={() => setIsSettingsOpen(true)} />
</header>
<main className="flex flex-col gap-8 items-center">
<div className="flex flex-wrap justify-center gap-8">
{schemes.map((scheme, index) => (
<ColorSchemeCard
key={index}
scheme={scheme}
isSelected={selectedSchemes.includes(index)}
onSelect={() => handleSchemeSelect(index)}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
))}
</div>
<ActionButton
onClick={handleAction}
label={selectedSchemes.length === 2 ? 'Mix' : 'Shuffle'}
/>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="https://nextjs.org/icons/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
<Settings
isDarkMode={isDarkMode}
onToggleDarkMode={toggleDarkMode}
isOpen={isSettingsOpen}
onClose={() => setIsSettingsOpen(false)}
/>
</div>
);
}

150
app/utils/colorSchemes.ts Normal file
View File

@ -0,0 +1,150 @@
type Color = string; // Hex color code
type ColorScheme = {
name: string;
colors: {
primary: { background: Color; foreground: Color };
normal: {
black: Color; red: Color; green: Color; yellow: Color;
blue: Color; magenta: Color; cyan: Color; white: Color;
};
bright: {
black: Color; red: Color; green: Color; yellow: Color;
blue: Color; magenta: Color; cyan: Color; white: Color;
};
};
};
import knownSchemesData from '../../formatted_themes.json';
const knownSchemes: ColorScheme[] = knownSchemesData;
function generateRandomColor(): Color {
return '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
}
function generateCreativeName(): string {
const adjectives = ['Cosmic', 'Neon', 'Mystic', 'Retro', 'Cyber', 'Ethereal', 'Vibrant', 'Dreamy', 'Futuristic', 'Nostalgic'];
const nouns = ['Sunset', 'Aurora', 'Galaxy', 'Ocean', 'Forest', 'Desert', 'Nebula', 'Horizon', 'Oasis', 'Metropolis'];
return `${adjectives[Math.floor(Math.random() * adjectives.length)]} ${nouns[Math.floor(Math.random() * nouns.length)]}`;
}
function generateRandomScheme(): ColorScheme {
let x = {
name: generateCreativeName(),
colors: {
primary: { background: generateRandomColor(), foreground: generateRandomColor() },
normal: {
black: generateRandomColor(), red: generateRandomColor(), green: generateRandomColor(), yellow: generateRandomColor(),
blue: generateRandomColor(), magenta: generateRandomColor(), cyan: generateRandomColor(), white: generateRandomColor()
},
bright: {
black: generateRandomColor(), red: generateRandomColor(), green: generateRandomColor(), yellow: generateRandomColor(),
blue: generateRandomColor(), magenta: generateRandomColor(), cyan: generateRandomColor(), white: generateRandomColor()
}
}
};
x.colors.primary.background = x.colors.normal.black;
x.colors.primary.foreground = x.colors.bright.white;
return x;
}
function crossTitles(title1: string, title2: string): string {
const words1 = title1.split(' ');
const words2 = title2.split(' ');
const firstWord = Math.random() < 0.5 ? words1[0] : words2[1];
const secondWord = Math.random() < 0.5 ? words2[0] : words1[1];
return `${firstWord} ${secondWord}`;
}
function crossSchemes(scheme1: ColorScheme, scheme2: ColorScheme): ColorScheme {
const crossColor = (color1: Color, color2: Color): Color => {
const r = Math.round((parseInt(color1.slice(1, 3), 16) + parseInt(color2.slice(1, 3), 16)) / 2);
const g = Math.round((parseInt(color1.slice(3, 5), 16) + parseInt(color2.slice(3, 5), 16)) / 2);
const b = Math.round((parseInt(color1.slice(5, 7), 16) + parseInt(color2.slice(5, 7), 16)) / 2);
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
};
return {
name: crossTitles(scheme1.name, scheme2.name),
colors: {
primary: {
background: crossColor(scheme1.colors.primary.background, scheme2.colors.primary.background),
foreground: crossColor(scheme1.colors.primary.foreground, scheme2.colors.primary.foreground)
},
normal: {
black: crossColor(scheme1.colors.normal.black, scheme2.colors.normal.black),
red: crossColor(scheme1.colors.normal.red, scheme2.colors.normal.red),
green: crossColor(scheme1.colors.normal.green, scheme2.colors.normal.green),
yellow: crossColor(scheme1.colors.normal.yellow, scheme2.colors.normal.yellow),
blue: crossColor(scheme1.colors.normal.blue, scheme2.colors.normal.blue),
magenta: crossColor(scheme1.colors.normal.magenta, scheme2.colors.normal.magenta),
cyan: crossColor(scheme1.colors.normal.cyan, scheme2.colors.normal.cyan),
white: crossColor(scheme1.colors.normal.white, scheme2.colors.normal.white)
},
bright: {
black: crossColor(scheme1.colors.bright.black, scheme2.colors.bright.black),
red: crossColor(scheme1.colors.bright.red, scheme2.colors.bright.red),
green: crossColor(scheme1.colors.bright.green, scheme2.colors.bright.green),
yellow: crossColor(scheme1.colors.bright.yellow, scheme2.colors.bright.yellow),
blue: crossColor(scheme1.colors.bright.blue, scheme2.colors.bright.blue),
magenta: crossColor(scheme1.colors.bright.magenta, scheme2.colors.bright.magenta),
cyan: crossColor(scheme1.colors.bright.cyan, scheme2.colors.bright.cyan),
white: crossColor(scheme1.colors.bright.white, scheme2.colors.bright.white)
}
}
};
}
function mutateColor(color: Color): Color {
const r = parseInt(color.slice(1, 3), 16);
const g = parseInt(color.slice(3, 5), 16);
const b = parseInt(color.slice(5, 7), 16);
const mutateComponent = (component: number) => {
const mutation = Math.floor(Math.random() * 51) - 25; // Random number between -25 and 25
return Math.max(0, Math.min(255, component + mutation));
};
const newR = mutateComponent(r);
const newG = mutateComponent(g);
const newB = mutateComponent(b);
return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`;
}
function generateSchemeFromGeneticAlgorithm(likedSchemes: ColorScheme[], dislikedSchemes: ColorScheme[]): ColorScheme {
if (likedSchemes.length === 0) {
return generateRandomScheme();
}
const parentScheme = likedSchemes[Math.floor(Math.random() * likedSchemes.length)];
const newScheme: ColorScheme = JSON.parse(JSON.stringify(parentScheme)); // Deep copy
// Mutate colors
Object.keys(newScheme.colors).forEach((colorGroup) => {
Object.keys(newScheme.colors[colorGroup]).forEach((colorName) => {
if (Math.random() < 0.3) { // 30% chance of mutation
newScheme.colors[colorGroup][colorName] = mutateColor(newScheme.colors[colorGroup][colorName]);
}
});
});
// Avoid similarities with disliked schemes
dislikedSchemes.forEach(dislikedScheme => {
Object.keys(newScheme.colors).forEach((colorGroup) => {
Object.keys(newScheme.colors[colorGroup]).forEach((colorName) => {
if (newScheme.colors[colorGroup][colorName] === dislikedScheme.colors[colorGroup][colorName]) {
newScheme.colors[colorGroup][colorName] = mutateColor(newScheme.colors[colorGroup][colorName]);
}
});
});
});
newScheme.name = generateCreativeName();
return newScheme;
}
export type { ColorScheme };
export { knownSchemes, generateRandomScheme, crossSchemes, generateSchemeFromGeneticAlgorithm };

32
app/utils/yamlExport.ts Normal file
View File

@ -0,0 +1,32 @@
import { ColorScheme } from './colorSchemes';
export function generateYAML(scheme: ColorScheme): string {
return `colors:
# Default colors
primary:
background: '${scheme.colors.primary.background}'
foreground: '${scheme.colors.primary.foreground}'
# Normal colors
normal:
black: '${scheme.colors.normal.black}'
red: '${scheme.colors.normal.red}'
green: '${scheme.colors.normal.green}'
yellow: '${scheme.colors.normal.yellow}'
blue: '${scheme.colors.normal.blue}'
magenta: '${scheme.colors.normal.magenta}'
cyan: '${scheme.colors.normal.cyan}'
white: '${scheme.colors.normal.white}'
# Bright colors
bright:
black: '${scheme.colors.bright.black}'
red: '${scheme.colors.bright.red}'
green: '${scheme.colors.bright.green}'
yellow: '${scheme.colors.bright.yellow}'
blue: '${scheme.colors.bright.blue}'
magenta: '${scheme.colors.bright.magenta}'
cyan: '${scheme.colors.bright.cyan}'
white: '${scheme.colors.bright.white}'
`;
}

9485
formatted_themes.json Normal file

File diff suppressed because it is too large Load Diff

29
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"next": "14.2.8",
"prism-react-renderer": "^2.4.0",
"react": "^18",
"react-dom": "^18"
},
@ -507,6 +508,12 @@
"undici-types": "~6.19.2"
}
},
"node_modules/@types/prismjs": {
"version": "1.26.4",
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz",
"integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==",
"license": "MIT"
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
@ -1271,6 +1278,15 @@
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT"
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -4128,6 +4144,19 @@
"node": ">= 0.8.0"
}
},
"node_modules/prism-react-renderer": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz",
"integrity": "sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==",
"license": "MIT",
"dependencies": {
"@types/prismjs": "^1.26.0",
"clsx": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.0.0"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",

View File

@ -9,18 +9,19 @@
"lint": "next lint"
},
"dependencies": {
"next": "14.2.8",
"prism-react-renderer": "^2.4.0",
"react": "^18",
"react-dom": "^18",
"next": "14.2.8"
"react-dom": "^18"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.8",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"eslint": "^8",
"eslint-config-next": "14.2.8"
"typescript": "^5"
}
}

View File

@ -12,6 +12,15 @@ const config: Config = {
background: "var(--background)",
foreground: "var(--foreground)",
},
animation: {
float: 'float 3s ease-in-out infinite',
},
keyframes: {
float: {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
}
}
},
},
plugins: [],

75
themes_downloader.py Normal file
View File

@ -0,0 +1,75 @@
import requests
import json
# Base URL for the Gogh JSON files in the GitHub repository
base_url = "https://raw.githubusercontent.com/Gogh-Co/Gogh/master/json/"
# GitHub API URL to list the contents of the "json" directory
api_url = "https://api.github.com/repos/Gogh-Co/Gogh/contents/json"
# Function to scrape all theme filenames from the GitHub API
def fetch_theme_filenames():
response = requests.get(api_url)
if response.status_code == 200:
files = response.json()
# Filter JSON files and return their names
return [file["name"] for file in files if file["name"].endswith(".json")]
else:
print(f"Failed to fetch theme filenames from GitHub API, status code: {response.status_code}")
return []
# Helper function to map Gogh colors to the desired format
def map_theme_colors(theme_data):
return {
"name": theme_data.get("name", "Unknown Theme"),
"colors": {
"primary": {
"background": theme_data.get("background", "#000000"),
"foreground": theme_data.get("foreground", "#FFFFFF"),
},
"normal": {
"black": theme_data.get("color_01", "#000000"),
"red": theme_data.get("color_02", "#000000"),
"green": theme_data.get("color_03", "#000000"),
"yellow": theme_data.get("color_04", "#000000"),
"blue": theme_data.get("color_05", "#000000"),
"magenta": theme_data.get("color_06", "#000000"),
"cyan": theme_data.get("color_07", "#000000"),
"white": theme_data.get("color_08", "#000000"),
},
"bright": {
"black": theme_data.get("color_09", "#000000"),
"red": theme_data.get("color_10", "#000000"),
"green": theme_data.get("color_11", "#000000"),
"yellow": theme_data.get("color_12", "#000000"),
"blue": theme_data.get("color_13", "#000000"),
"magenta": theme_data.get("color_14", "#000000"),
"cyan": theme_data.get("color_15", "#000000"),
"white": theme_data.get("color_16", "#000000"),
}
}
}
# List to hold all the themes
themes = []
# Fetch all theme filenames from the GitHub API
theme_filenames = fetch_theme_filenames()
# Loop through the list of theme filenames and fetch each theme
for filename in theme_filenames:
theme_url = base_url + filename
response = requests.get(theme_url)
if response.status_code == 200:
theme_data = response.json()
formatted_theme = map_theme_colors(theme_data)
themes.append(formatted_theme)
else:
print(f"Failed to fetch {filename}")
# Output the formatted themes as a JSON array
output_file = "formatted_themes.json"
with open(output_file, "w") as out_file:
json.dump(themes, out_file, indent=2)
print(f"Formatted themes saved to {output_file}")