colors and junk

This commit is contained in:
billy 2025-03-30 15:16:32 -04:00
parent a25fa4a94c
commit da79c678ec
10 changed files with 438 additions and 62 deletions

81
analyze_key_images.py Normal file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env python3
from PIL import Image
import numpy as np
from collections import Counter
import colorsys
def rgb_to_hex(rgb):
return '#{:02x}{:02x}{:02x}'.format(rgb[0], rgb[1], rgb[2])
def get_dominant_colors(image_path, num_colors=8):
try:
# Open image and convert to RGB
img = Image.open(image_path).convert('RGB')
# Resize image to make processing faster
img = img.resize((150, 150))
# Get colors
pixels = np.array(img)
pixels = pixels.reshape(-1, 3)
# Count most common colors
count = Counter(map(tuple, pixels))
most_common = count.most_common(num_colors)
# Convert to hex
return [rgb_to_hex(color) for color, count in most_common]
except Exception as e:
print(f"Error processing {image_path}: {e}")
return []
# List of key images to analyze
key_images = [
"/home/billy/code/viber/src/assets/logo.png",
"/home/billy/code/viber/src/assets/mouse.png",
"/home/billy/code/viber/src/assets/keyboard.png",
"/home/billy/code/viber/src/assets/monitor.png",
"/home/billy/code/viber/src/assets/data center.png",
"/home/billy/code/viber/src/assets/galaxy.png"
]
print("Key Image Analysis:\n")
for image_path in key_images:
image_name = image_path.split("/")[-1]
colors = get_dominant_colors(image_path)
print(f"{image_name}:")
for color in colors:
# Convert to RGB for displaying color names
h = color.lstrip('#')
r, g, b = tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255)
# Determine color category
category = "unknown"
if s < 0.15:
if v < 0.15:
category = "black"
elif v > 0.85:
category = "white"
else:
category = "gray"
else:
if h < 0.05 or h > 0.95:
category = "red"
elif 0.05 <= h < 0.15:
category = "orange"
elif 0.15 <= h < 0.25:
category = "yellow"
elif 0.25 <= h < 0.5:
category = "green"
elif 0.5 <= h < 0.65:
category = "cyan"
elif 0.65 <= h < 0.75:
category = "blue"
elif 0.75 <= h < 0.95:
category = "purple"
print(f" {color} - {category} (HSV: {h*360:.1f}, {s*100:.1f}%, {v*100:.1f}%)")
print("")

155
color_analysis.py Normal file
View File

@ -0,0 +1,155 @@
#!/usr/bin/env python3
from PIL import Image
import numpy as np
import os
from collections import Counter
import colorsys
def rgb_to_hex(rgb):
return '#{:02x}{:02x}{:02x}'.format(rgb[0], rgb[1], rgb[2])
def get_dominant_colors(image_path, num_colors=5):
# Open image and convert to RGB
img = Image.open(image_path).convert('RGB')
# Resize image to make processing faster
img = img.resize((100, 100))
# Get colors
pixels = np.array(img)
pixels = pixels.reshape(-1, 3)
# Count most common colors
count = Counter(map(tuple, pixels))
most_common = count.most_common(num_colors)
# Convert to hex
return [rgb_to_hex(color) for color, count in most_common]
def get_color_stats(hex_color):
# Convert hex to RGB
h = hex_color.lstrip('#')
r, g, b = tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
# Convert RGB to HSV
h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255)
# Determine if color is dark or light
is_dark = v < 0.5
# Determine if color is vibrant
is_vibrant = s > 0.5 and v > 0.5
# Determine basic color category
if s < 0.15:
if v < 0.15:
category = "black"
elif v > 0.85:
category = "white"
else:
category = "gray"
else:
if h < 0.05 or h > 0.95:
category = "red"
elif 0.05 <= h < 0.15:
category = "orange"
elif 0.15 <= h < 0.25:
category = "yellow"
elif 0.25 <= h < 0.5:
category = "green"
elif 0.5 <= h < 0.65:
category = "cyan"
elif 0.65 <= h < 0.75:
category = "blue"
elif 0.75 <= h < 0.95:
category = "purple"
return {
"hex": hex_color,
"rgb": (r, g, b),
"hsv": (h*360, s*100, v*100),
"is_dark": is_dark,
"is_vibrant": is_vibrant,
"category": category
}
def analyze_assets(directory):
assets = []
# Find all PNG files
for file in os.listdir(directory):
if file.endswith('.png'):
assets.append(os.path.join(directory, file))
# Get color data
color_data = {}
all_colors = []
for asset in assets:
name = os.path.basename(asset)
dominant_colors = get_dominant_colors(asset)
color_data[name] = dominant_colors
all_colors.extend(dominant_colors)
# Count most common colors across all assets
color_counter = Counter(all_colors)
most_common_colors = color_counter.most_common(10)
# Get stats for most common colors
color_stats = [get_color_stats(color) for color, _ in most_common_colors]
# Find primary and accent colors
primary_colors = []
accent_colors = []
for stat in color_stats:
if stat["category"] not in ["black", "white", "gray"]:
if stat["is_vibrant"]:
accent_colors.append(stat)
else:
primary_colors.append(stat)
# Find background colors (darker colors)
background_colors = [stat for stat in color_stats if stat["is_dark"]]
return {
"individual_assets": color_data,
"most_common_colors": [color for color, _ in most_common_colors],
"color_stats": color_stats,
"primary_colors": primary_colors[:3],
"accent_colors": accent_colors[:3],
"background_colors": background_colors[:3]
}
if __name__ == "__main__":
assets_dir = "/home/billy/code/viber/src/assets"
results = analyze_assets(assets_dir)
print("Color Analysis Results:\n")
print("Most Common Colors:")
for color in results["most_common_colors"]:
print(f" {color}")
print("\nPrimary Colors:")
for color in results["primary_colors"]:
print(f" {color['hex']} - {color['category']}")
print("\nAccent Colors:")
for color in results["accent_colors"]:
print(f" {color['hex']} - {color['category']}")
print("\nBackground Colors:")
for color in results["background_colors"]:
print(f" {color['hex']} - {color['category']}")
print("\nDetailed Color Stats:")
for i, stat in enumerate(results["color_stats"][:5]):
print(f"Color {i+1}:")
print(f" Hex: {stat['hex']}")
print(f" RGB: {stat['rgb']}")
print(f" HSV: {stat['hsv']}")
print(f" Category: {stat['category']}")
print(f" Dark: {stat['is_dark']}")
print(f" Vibrant: {stat['is_vibrant']}")
print("")

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -88,7 +88,7 @@ function App() {
<ChakraProvider theme={theme}>
<Box
minH="100vh"
bg="gray.900"
bg="background.primary"
color="white"
onClick={handleClick}
cursor={hasStarted ? "pointer" : "default"}
@ -130,14 +130,14 @@ function App() {
size="2xl"
>
<ModalOverlay />
<ModalContent bg="gray.800" color="white" maxW="800px">
<ModalHeader borderBottom="1px" borderColor="gray.700" pb={4}>
<ModalContent bg="background.secondary" color="white" maxW="800px">
<ModalHeader borderBottom="1px" borderColor="brand.700" pb={4}>
Welcome to ClickerCorp
</ModalHeader>
<ModalBody py={6}>
<VStack spacing={6} align="stretch">
<Box>
<Text fontSize="lg" fontWeight="bold" mb={2}>
<Text fontSize="lg" fontWeight="bold" mb={2} color="brand.300">
Employment Agreement
</Text>
<Text fontSize="md" color="gray.300" lineHeight="1.6">
@ -149,7 +149,7 @@ function App() {
</Box>
<Box>
<Text fontSize="lg" fontWeight="bold" mb={2}>
<Text fontSize="lg" fontWeight="bold" mb={2} color="brand.300">
Controls & Operations
</Text>
<Text fontSize="md" color="gray.300" lineHeight="1.6">
@ -158,13 +158,13 @@ function App() {
</Box>
<Box>
<Text fontSize="sm" color="gray.400" fontStyle="italic">
<Text fontSize="sm" color="brand.200" fontStyle="italic">
(Please note: This position is unpaid and offers exposure as compensation)
</Text>
</Box>
<Checkbox
colorScheme="cyan"
colorScheme="brand"
isChecked={agreedToTerms}
onChange={(e) => setAgreedToTerms(e.target.checked)}
>
@ -172,9 +172,9 @@ function App() {
</Checkbox>
</VStack>
</ModalBody>
<ModalFooter borderTop="1px" borderColor="gray.700" pt={4}>
<ModalFooter borderTop="1px" borderColor="brand.700" pt={4}>
<Button
colorScheme="cyan"
colorScheme="brand"
isDisabled={!agreedToTerms}
onClick={handleStartGame}
w="full"

View File

@ -104,11 +104,17 @@ export function BuildingButton({
return (
<Box
bg="gray.700"
bg="background.card"
p={4}
borderRadius="lg"
border="1px"
borderColor="gray.600"
borderColor="brand.700"
boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.24)"
transition="all 0.2s ease-in-out"
_hover={{
transform: "translateY(-2px)",
boxShadow: "0 6px 10px -1px rgba(0, 0, 0, 0.4), 0 4px 6px -1px rgba(0, 0, 0, 0.3)"
}}
>
<VStack align="stretch" spacing={2}>
{/* Image with overlapping title and stats */}
@ -117,6 +123,8 @@ export function BuildingButton({
<Box
borderRadius="md"
overflow="hidden"
borderWidth="2px"
borderColor="brand.800"
>
<Image
src={buildingImages[buildingType as keyof typeof buildingImages]}
@ -134,11 +142,12 @@ export function BuildingButton({
top="0"
left="0"
p={2}
bg="blackAlpha.700"
bg="background.cardOverlay"
borderTopLeftRadius="md"
borderBottomRightRadius="md"
boxShadow="0 1px 3px rgba(0,0,0,0.5)"
>
<Text fontWeight="bold" fontSize="md">{title}</Text>
<Text fontWeight="bold" fontSize="md" color="brand.200">{title}</Text>
</Box>
{/* Overlay for owned count */}
@ -147,11 +156,12 @@ export function BuildingButton({
bottom="0"
right="0"
p={2}
bg="blackAlpha.700"
bg="background.cardOverlay"
borderTopLeftRadius="md"
borderBottomRightRadius="md"
boxShadow="0 1px 3px rgba(0,0,0,0.5)"
>
<Text fontSize="sm">Owned: {formatNumber(owned)}</Text>
<Text fontSize="sm" color="brand.100">Owned: {formatNumber(owned)}</Text>
</Box>
{/* Level badge */}
@ -160,29 +170,32 @@ export function BuildingButton({
top="0"
right="0"
p={2}
bg="blue.700"
bg="brand.400"
borderTopRightRadius="md"
borderBottomLeftRadius="md"
boxShadow="0 1px 3px rgba(0,0,0,0.5)"
>
<Text fontSize="xs" fontWeight="bold">Level {level}</Text>
<Text fontSize="xs" fontWeight="bold" color="text.dark">Level {level}</Text>
</Box>
</Box>
<Text fontSize="sm" color="gray.400">{description}</Text>
<Box>
<Text fontSize="sm" color="text.secondary">{description}</Text>
<Box p={2} bg="background.secondary" borderRadius="md">
<Flex justifyContent="space-between" alignItems="center" mb={1}>
<Text fontSize="sm" fontWeight="bold">Production:</Text>
<Text fontSize="sm" fontWeight="bold" color="brand.300">Production:</Text>
</Flex>
<Text fontSize="sm">Points: {formatNumber(pointsPerSecond * level)}/s per building</Text>
<Text fontSize="sm">Total: {formatNumber(pointsPerSecond * level * owned)}/s</Text>
<Text fontSize="sm" color="text.primary">Points: {formatNumber(pointsPerSecond * level)}/s per building</Text>
<Text fontSize="sm" color="text.primary">Total: {formatNumber(pointsPerSecond * level * owned)}/s</Text>
</Box>
<HStack spacing={2}>
<Button
onClick={onClick}
opacity={isDisabledStyle ? 0.4 : 1}
_hover={{ bg: 'blue.600', opacity: isDisabledStyle ? 0.4 : 1 }}
_hover={{ bg: 'brand.500', opacity: isDisabledStyle ? 0.4 : 1 }}
cursor={isDisabledStyle ? 'not-allowed' : 'pointer'}
colorScheme="blue"
colorScheme="brand"
bg="brand.400"
color="text.dark"
size="sm"
flexGrow={1}
>
@ -192,11 +205,12 @@ export function BuildingButton({
<Button
onClick={() => upgradeBuilding(buildingType as keyof typeof buildingImages)}
opacity={!canUpgrade ? 0.4 : 1}
_hover={{ bg: 'green.600', opacity: !canUpgrade ? 0.4 : 1 }}
_hover={{ bg: 'background.highlight', opacity: !canUpgrade ? 0.4 : 1 }}
cursor={!canUpgrade ? 'not-allowed' : 'pointer'}
colorScheme="green"
colorScheme="brand"
size="sm"
flexGrow={1}
variant="outline"
>
Upgrade ({formatNumber(upgradeCost)} points)
</Button>

View File

@ -34,27 +34,28 @@ export function NextBuildingPreview() {
return (
<Tooltip label={`Requires level ${info.levelRequirement}`}>
<Box
bg="gray.800"
bg="background.card"
p={4}
borderRadius="lg"
border="1px"
borderColor="gray.700"
borderColor="brand.800"
opacity={0.7}
cursor="not-allowed"
boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.14)"
>
<VStack align="stretch" spacing={2}>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Text fontWeight="bold">{info.title}</Text>
<Badge colorScheme="red">Locked</Badge>
<Text fontWeight="bold" color="brand.200">{info.title}</Text>
<Badge colorScheme="brand" bg="brand.400" color="text.dark">Locked</Badge>
</Box>
<Text fontSize="sm" color="gray.400">{info.description}</Text>
<Text fontSize="sm" color="text.muted">{info.description}</Text>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Text>Cost: {formatNumber(info.cost)} points</Text>
<Text>Level Required: {info.levelRequirement}</Text>
<Text color="text.secondary">Cost: {formatNumber(info.cost)} points</Text>
<Text color="brand.300">Level Required: {info.levelRequirement}</Text>
</Box>
<Box>
<Text fontSize="sm">Production:</Text>
<Text fontSize="sm">Points: {formatNumber(info.production.points || 0)}/s</Text>
<Box p={2} bg="background.secondary" borderRadius="md">
<Text fontSize="sm" color="brand.300">Production:</Text>
<Text fontSize="sm" color="text.secondary">Points: {formatNumber(info.production.points || 0)}/s</Text>
</Box>
</VStack>
</Box>

View File

@ -38,8 +38,8 @@ export function ResetButton() {
onClose={onClose}
>
<AlertDialogOverlay>
<AlertDialogContent bg="gray.800" color="white">
<AlertDialogHeader fontSize="lg" fontWeight="bold">
<AlertDialogContent bg="background.secondary" color="white">
<AlertDialogHeader fontSize="lg" fontWeight="bold" color="brand.300">
Reset Game
</AlertDialogHeader>
@ -48,7 +48,7 @@ export function ResetButton() {
</AlertDialogBody>
<AlertDialogFooter>
<Button ref={cancelRef} onClick={onClose}>
<Button ref={cancelRef} onClick={onClose} colorScheme="brand" variant="outline">
Cancel
</Button>
<Button colorScheme="red" onClick={handleReset} ml={3}>

View File

@ -33,11 +33,11 @@ export function ResourceDisplay() {
left={0}
right={0}
zIndex={100}
bg="gray.900"
bg="background.card"
py={2}
borderBottom="1px"
borderColor="gray.700"
shadow="lg"
borderColor="brand.800"
boxShadow="0 2px 10px rgba(0,0,0,0.4)"
>
<Flex
maxW="1200px"
@ -60,6 +60,7 @@ export function ResourceDisplay() {
h="100%"
w="auto"
objectFit="contain"
filter="drop-shadow(0 2px 4px rgba(0,0,0,0.3))"
/>
</Box>
@ -87,30 +88,31 @@ export function ResourceDisplay() {
>
{/* Points */}
<Box textAlign="center" minW="120px">
<Text fontSize="sm" fontWeight="bold">Points</Text>
<Text fontSize="md" fontWeight="bold">{formatNumber(Math.floor(points))}</Text>
<Text fontSize="xs" color="green.400">+{formatNumber(pointsPerSecond)}/s</Text>
<Text fontSize="sm" fontWeight="bold" color="brand.300">Points</Text>
<Text fontSize="md" fontWeight="bold" color="text.primary">{formatNumber(Math.floor(points))}</Text>
<Text fontSize="xs" color="text.secondary">+{formatNumber(pointsPerSecond)}/s</Text>
</Box>
<Divider orientation="vertical" h="40px" />
<Divider orientation="vertical" h="40px" borderColor="brand.700" />
{/* Click Power with Upgrade Button */}
<Box textAlign="center" minW="120px">
<Text fontSize="sm" fontWeight="bold">Click Power</Text>
<Text fontSize="sm" fontWeight="bold" color="brand.300">Click Power</Text>
<Flex justify="center" align="center" gap={1}>
<Text fontSize="md">{formatNumber(clickPower)}</Text>
<Text fontSize="md" color="text.primary">{formatNumber(clickPower)}</Text>
<Tooltip label={`Upgrade to level ${clickPower + 1} (${formatNumber(clickPowerUpgradeCost)} points)`}>
<Button
size="xs"
colorScheme="green"
variant="outline"
colorScheme="brand"
bg="brand.400"
color="text.dark"
p={1}
height="20px"
minW="20px"
onClick={() => buyUpgrade('clickPower')}
opacity={canAffordUpgrade ? 1 : 0.4}
cursor={canAffordUpgrade ? 'pointer' : 'not-allowed'}
_hover={{ bg: 'green.800', opacity: canAffordUpgrade ? 1 : 0.4 }}
_hover={{ bg: 'brand.500', opacity: canAffordUpgrade ? 1 : 0.4 }}
>
<AddIcon boxSize={2} />
</Button>
@ -118,27 +120,27 @@ export function ResourceDisplay() {
</Flex>
</Box>
<Divider orientation="vertical" h="40px" />
<Divider orientation="vertical" h="40px" borderColor="brand.700" />
{/* Level */}
<Box textAlign="center" minW="80px">
<Text fontSize="sm" fontWeight="bold">Level</Text>
<Text fontSize="md">{playerLevel}</Text>
<Text fontSize="sm" fontWeight="bold" color="brand.300">Level</Text>
<Text fontSize="md" color="text.primary">{playerLevel}</Text>
</Box>
{/* Progress to next level */}
<Tooltip label={`${formatNumber(pointsPerSecond)}/${formatNumber(nextLevelPPS)} PPS`}>
<Box flex="1" maxW="300px">
<Flex justify="space-between" mb={1}>
<Text fontSize="xs" color="gray.400">Level {playerLevel}</Text>
<Text fontSize="xs" color="gray.400">Level {playerLevel + 1}</Text>
<Text fontSize="xs" color="text.secondary">Level {playerLevel}</Text>
<Text fontSize="xs" color="text.secondary">Level {playerLevel + 1}</Text>
</Flex>
<Progress
value={progress}
colorScheme="green"
colorScheme="brand"
size="sm"
borderRadius="full"
bg="gray.700"
bg="background.secondary"
/>
</Box>
</Tooltip>

View File

@ -20,7 +20,7 @@ export function SoundToggleButton() {
icon={<span>{soundOn ? '🔊' : '🔇'}</span>}
onClick={handleToggle}
variant="ghost"
colorScheme="cyan"
colorScheme="brand"
size="md"
/>
</Tooltip>

View File

@ -1,22 +1,145 @@
import { extendTheme } from '@chakra-ui/react'
// Color palette based on asset analysis but with improved contrast
const colors = {
brand: {
// Main teal/cyan color found in assets
50: '#e3f9fb',
100: '#c5eef1',
200: '#a1e0e5',
300: '#7ad0d7',
400: '#51bec7',
500: '#29686c', // Our primary accent from analysis
600: '#1c5458', // Darker variant
700: '#183d3f', // From keyboard.png
800: '#113738', // From mouse.png
900: '#0f1e41', // From logo.png - more blue variant
},
background: {
primary: '#000101', // Almost black with slight cyan tint
secondary: '#0a141e', // Darker blue for better contrast
tertiary: '#122c2e', // Darker cyan for cards
card: '#112233', // Darker blue to contrast with teal images
highlight: '#1d4244', // For hover states and highlights
cardOverlay: 'rgba(17, 34, 51, 0.85)', // Semi-transparent overlay for text on images
},
text: {
primary: '#ffffff',
secondary: '#a1e0e5', // Light brand color for secondary text
accent: '#51bec7', // Brighter cyan for accents and highlights
muted: '#718096', // For less important text
dark: '#0a141e', // Dark text for light backgrounds
}
}
const theme = extendTheme({
config: {
initialColorMode: 'dark',
useSystemColorMode: false,
},
colors,
styles: {
global: {
body: {
bg: 'gray.900',
color: 'white',
bg: colors.background.primary,
color: colors.text.primary,
},
},
},
components: {
Button: {
defaultProps: {
colorScheme: 'blue',
colorScheme: 'brand',
},
variants: {
solid: {
bg: 'brand.500',
color: 'white',
_hover: {
bg: 'brand.600',
},
},
outline: {
borderColor: 'brand.300',
color: 'text.primary',
_hover: {
bg: 'background.highlight',
},
},
ghost: {
color: 'brand.300',
_hover: {
bg: 'background.highlight',
}
}
},
},
Box: {
variants: {
card: {
bg: 'background.card',
borderRadius: 'lg',
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.24)',
borderColor: 'brand.700',
borderWidth: '1px',
transition: 'all 0.2s ease-in-out',
_hover: {
transform: 'translateY(-2px)',
boxShadow: '0 6px 10px -1px rgba(0, 0, 0, 0.4), 0 4px 6px -1px rgba(0, 0, 0, 0.3)',
},
},
},
},
Progress: {
baseStyle: {
filledTrack: {
bg: 'brand.400',
},
track: {
bg: 'background.highlight',
},
},
},
Tooltip: {
baseStyle: {
bg: 'background.secondary',
color: 'text.secondary',
borderColor: 'brand.700',
borderWidth: '1px',
},
},
Heading: {
baseStyle: {
color: 'text.secondary',
},
},
Text: {
variants: {
accent: {
color: 'text.accent',
},
secondary: {
color: 'text.secondary',
},
muted: {
color: 'text.muted',
},
},
},
Modal: {
baseStyle: {
dialog: {
bg: 'background.secondary',
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -2px rgba(0, 0, 0, 0.4)',
borderWidth: '1px',
borderColor: 'brand.700',
},
header: {
color: 'text.secondary',
},
body: {
color: 'text.primary',
},
},
},
},