diff --git a/Makefile b/Makefile index 1743939..6b719bb 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ CONTAINER_NAME = logo-gallery DEV_PORT = 5006 # Main targets -.PHONY: all build start stop restart logs clean scan-logos dev rebuild favicon deps-favicon build-with-favicons sync-packages +.PHONY: all build start stop restart logs clean scan-logos dev rebuild favicon deps-favicon build-with-favicons sync-packages convert-colors all: build start @@ -50,6 +50,12 @@ scan-logos: $(DOCKER_COMPOSE) -f compose.dev.yml run --rm slogos-dev npm run scan-logos @echo "Logos have been updated - refresh the browser to see changes" +# Convert logo colors format from array to object +convert-colors: + @echo "Converting logo colors format in logos.json..." + $(DOCKER_COMPOSE) -f compose.dev.yml run --rm slogos-dev npm run convert-colors + @echo "Color format conversion complete" + # Clean up build artifacts and temporary files clean: @echo "Cleaning up build artifacts and temporary files..." diff --git a/package.json b/package.json index 9addf3e..e2fb8fe 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "rollup -c -w", "start": "sirv public --host 0.0.0.0 --dev --single", "scan-logos": "node scripts/scanLogos.js", - "generate-favicons": "node scripts/generateFavicons.js" + "generate-favicons": "node scripts/generateFavicons.js", + "convert-colors": "node scripts/convertColorsFormat.js" }, "devDependencies": { "@rollup/plugin-commonjs": "^17.0.0", diff --git a/public/data/logos.json b/public/data/logos.json index 013dff4..ea95b5c 100644 --- a/public/data/logos.json +++ b/public/data/logos.json @@ -30,18 +30,23 @@ "tags": [ "tech" ], - "colors": [ - { - "label": "Silver", - "value": "#999" - }, - { - "label": "Dark Grey", - "value": "#666" - } - ], + "colors": { + "silver": "#999", + "dark_grey": "#666" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "silver" + }, + "set_2": { + "main": "dark_grey" + } } }, { @@ -100,16 +105,21 @@ "tags": [ "tech" ], - "colors": [ - { - "label": "Dark Blue", - "value": "#080225" - } - ], + "colors": { + "dark_blue": "#080225" + }, "colorConfig": { "target": "g" }, - "brand": "arm" + "brand": "arm", + "targets": { + "main": "g" + }, + "sets": { + "set_1": { + "main": "dark_blue" + } + } }, { "name": "ATB", @@ -200,12 +210,9 @@ "garage" ], "brand": "Dalnoboy Service", - "colors": [ - { - "label": "Orange", - "value": "#ee7800" - } - ] + "colors": { + "orange": "#ee7800" + } }, { "name": "Debian", @@ -241,14 +248,19 @@ "container" ], "brand": "Docker", - "colors": [ - { - "label": "Blue", - "value": "#1d63ed" - } - ], + "colors": { + "blue": "#1d63ed" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "blue" + } } }, { @@ -261,19 +273,23 @@ "automobile", "transport" ], - "colors": [ - { - "label": "Grey", - "value": "#2b2a29", - "theme": "light" - }, - { - "label": "Red", - "value": "#e22b28" - } - ], + "colors": { + "grey": "#2b2a29", + "red": "#e22b28" + }, "colorConfig": { "selector": "#text" + }, + "targets": { + "selector_1": "#text" + }, + "sets": { + "set_1": { + "selector_1": "grey" + }, + "set_2": { + "selector_1": "red" + } } }, { @@ -353,14 +369,19 @@ "software" ], "brand": "GitHub", - "colors": [ - { - "label": "Dark Grey", - "value": "#24292f" - } - ], + "colors": { + "dark_grey": "#24292f" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "dark_grey" + } } }, { @@ -373,14 +394,19 @@ "software" ], "brand": "GitHub", - "colors": [ - { - "label": "Dark Grey", - "value": "#24292f" - } - ], + "colors": { + "dark_grey": "#24292f" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "dark_grey" + } } }, { @@ -404,30 +430,35 @@ "language" ], "brand": "Go", - "colors": [ - { - "label": "Aqua", - "value": "#2DBCAF" - }, - { - "label": "Blue", - "value": "#00ACD7" - }, - { - "label": "Fuchsia", - "value": "#CE3262" - }, - { - "label": "LightBlue", - "value": "#5DC9E1" - }, - { - "label": "Yellow", - "value": "#FDDD00" - } - ], + "colors": { + "aqua": "#2DBCAF", + "blue": "#00ACD7", + "fuchsia": "#CE3262", + "lightblue": "#5DC9E1", + "yellow": "#FDDD00" + }, "colorConfig": { "target": "g" + }, + "targets": { + "main": "g" + }, + "sets": { + "set_1": { + "main": "aqua" + }, + "set_2": { + "main": "blue" + }, + "set_3": { + "main": "fuchsia" + }, + "set_4": { + "main": "lightblue" + }, + "set_5": { + "main": "yellow" + } } }, { @@ -560,14 +591,19 @@ "automobile", "transport" ], - "colors": [ - { - "label": "Green", - "value": "#424d07" - } - ], + "colors": { + "green": "#424d07" + }, "colorConfig": { "target": "g" + }, + "targets": { + "main": "g" + }, + "sets": { + "set_1": { + "main": "green" + } } }, { @@ -602,14 +638,19 @@ "appliances" ], "brand": "LG", - "colors": [ - { - "label": "Grey", - "value": "#6b6c6b" - } - ], + "colors": { + "grey": "#6b6c6b" + }, "colorConfig": { "target": "#text" + }, + "targets": { + "main": "#text" + }, + "sets": { + "set_1": { + "main": "grey" + } } }, { @@ -644,16 +685,21 @@ "software", "messaging" ], - "colors": [ - { - "label": "Denim", - "value": "#1e325c" - } - ], + "colors": { + "denim": "#1e325c" + }, "colorConfig": { "target": "g" }, - "brand": "Mattermost" + "brand": "Mattermost", + "targets": { + "main": "g" + }, + "sets": { + "set_1": { + "main": "denim" + } + } }, { "name": "McDonald's", @@ -739,31 +785,33 @@ "bank", "finance" ], - "colors": [ - { - "label": "Black", - "value": "#000" - }, - { - "label": "Yellow", - "value": "#f2bd2b" - } - ], + "colors": { + "black": "#000", + "yellow": "#f2bd2b", + "white": "#fff" + }, "colorConfig": { "target": "#background" + }, + "targets": { + "background": "#background", + "text": "#text" + }, + "sets": { + "black": { + "background": "black", + "text": "white" + }, + "white": { + "background": "white", + "text": "black" + }, + "yellow": { + "background": "yellow", + "text": "white" + } } }, - { - "name": "Mono White", - "path": "logos/mono_white.svg", - "format": "SVG", - "disable": false, - "brand": "Monobank", - "tags": [ - "bank", - "finance" - ] - }, { "name": "Monobank paw", "path": "logos/monobank_paw.svg", @@ -1035,14 +1083,19 @@ "database" ], "brand": "Redis", - "colors": [ - { - "label": "Grey", - "value": "#636466" - } - ], + "colors": { + "grey": "#636466" + }, "colorConfig": { "selector": "#text" + }, + "targets": { + "selector_1": "#text" + }, + "sets": { + "set_1": { + "selector_1": "grey" + } } }, { @@ -1089,21 +1142,25 @@ "tags": [ "furniture" ], - "colors": [ - { - "label": "Brown", - "value": "#2b1c13", - "theme": "light" - }, - { - "label": "Green", - "value": "#859310" - } - ], + "colors": { + "brown": "#2b1c13", + "green": "#859310" + }, "colorConfig": { "target": "#text" }, - "brand": "RoomerIN" + "brand": "RoomerIN", + "targets": { + "main": "#text" + }, + "sets": { + "set_1": { + "main": "brown" + }, + "set_2": { + "main": "green" + } + } }, { "name": "Sainsbury's", @@ -1200,14 +1257,19 @@ "communication" ], "brand": "Signal", - "colors": [ - { - "label": "Ultramarine", - "value": "#3b45fd" - } - ], + "colors": { + "ultramarine": "#3b45fd" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "ultramarine" + } } }, { @@ -1220,14 +1282,19 @@ "communication" ], "brand": "Signal", - "colors": [ - { - "label": "Ultramarine", - "value": "#3b45fd" - } - ], + "colors": { + "ultramarine": "#3b45fd" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "ultramarine" + } } }, { @@ -1252,14 +1319,21 @@ "footwear" ], "brand": "Skechers", - "colors": [ - { - "label": "Deep Blue", - "value": "#062849" - } - ], + "colors": { + "deep_blue": "#062849" + }, "colorConfig": { "selector": "#text, #logo_int" + }, + "targets": { + "selector_1": "#text", + "selector_2": "#logo_int" + }, + "sets": { + "set_1": { + "selector_1": "deep_blue", + "selector_2": "deep_blue" + } } }, { @@ -1317,14 +1391,19 @@ "transport" ], "brand": "Tesla", - "colors": [ - { - "label": "Red", - "value": "#e82127" - } - ], + "colors": { + "red": "#e82127" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "red" + } } }, { @@ -1348,14 +1427,19 @@ "transport" ], "brand": "Toyota", - "colors": [ - { - "label": "Red", - "value": "#EB0A1E" - } - ], + "colors": { + "red": "#EB0A1E" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "red" + } } }, { @@ -1368,14 +1452,19 @@ "transport" ], "brand": "Toyota", - "colors": [ - { - "label": "Red", - "value": "#EB0A1E" - } - ], + "colors": { + "red": "#EB0A1E" + }, "colorConfig": { "target": "path" + }, + "targets": { + "main": "path" + }, + "sets": { + "set_1": { + "main": "red" + } } }, { diff --git a/public/logos/mono_white.svg b/public/logos/mono_white.svg deleted file mode 100644 index a3f6562..0000000 --- a/public/logos/mono_white.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - diff --git a/scripts/convertColorsFormat.js b/scripts/convertColorsFormat.js new file mode 100644 index 0000000..e8410e1 --- /dev/null +++ b/scripts/convertColorsFormat.js @@ -0,0 +1,93 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +// Path to logos.json +const logosJsonPath = path.join(__dirname, '../public/data/logos.json'); + +// Read the current logos.json file +console.log('Reading logos.json...'); +const logosData = JSON.parse(fs.readFileSync(logosJsonPath, 'utf8')); + +// Convert the colors format and create targets & sets +console.log('Converting logos format...'); +let convertedColorsCount = 0; +let convertedConfigCount = 0; + +for (const logo of logosData) { + // 1. Convert colors array to object if needed + if (logo.colors && Array.isArray(logo.colors)) { + // Convert array format to object format + const newColors = {}; + for (const colorObj of logo.colors) { + // Convert label to lowercase and replace spaces with underscores + const key = colorObj.label.toLowerCase().replace(/\s+/g, '_'); + newColors[key] = colorObj.value; + } + logo.colors = newColors; + convertedColorsCount++; + } + + // 2. Convert colorConfig to targets and sets + if (logo.colorConfig && !logo.targets) { + // Create targets object + logo.targets = {}; + + // Handle selector or target from colorConfig + if (logo.colorConfig.selector) { + // Split multiple selectors (e.g., "#text, #logo_int") + const selectors = logo.colorConfig.selector.split(',').map(s => s.trim()); + + // Create a target for each selector + selectors.forEach((selector, index) => { + logo.targets[`selector_${index + 1}`] = selector; + }); + + // Create sets for each color + if (logo.colors && Object.keys(logo.colors).length > 0) { + logo.sets = {}; + let setIndex = 1; + + // Create a set for each color + for (const [colorName, colorValue] of Object.entries(logo.colors)) { + const setName = `set_${setIndex}`; + logo.sets[setName] = {}; + + // Apply this color to all targets in this set + Object.keys(logo.targets).forEach(targetName => { + logo.sets[setName][targetName] = colorName; + }); + + setIndex++; + } + } + } else if (logo.colorConfig.target) { + logo.targets.main = logo.colorConfig.target; + + // Create sets for each color + if (logo.colors && Object.keys(logo.colors).length > 0) { + logo.sets = {}; + let setIndex = 1; + + // Create a set for each color + for (const [colorName, colorValue] of Object.entries(logo.colors)) { + const setName = `set_${setIndex}`; + logo.sets[setName] = { + main: colorName + }; + setIndex++; + } + } + } + + convertedConfigCount++; + + // Keep the original colorConfig for backward compatibility + } +} + +// Write the updated data back to logos.json +console.log('Writing updated logos.json...'); +fs.writeFileSync(logosJsonPath, JSON.stringify(logosData, null, 2)); +console.log(`Conversion complete! Updated colors for ${convertedColorsCount} logos and created targets/sets for ${convertedConfigCount} logos.`); diff --git a/scripts/scanLogos.js b/scripts/scanLogos.js index 70f0f53..ee4b8f6 100644 --- a/scripts/scanLogos.js +++ b/scripts/scanLogos.js @@ -171,6 +171,52 @@ function scanLogos() { if (!logoObj.brand) logoObj.brand = logoObj.name; if (!Array.isArray(logoObj.tags)) logoObj.tags = []; if (typeof logoObj.disable !== 'boolean') logoObj.disable = false; + + // Set default colorConfig, targets, and sets for SVGs + if (logoObj.format.toLowerCase() === 'svg') { + // Maintain backward compatibility + if (!logoObj.colorConfig) { + logoObj.colorConfig = { target: 'path', attribute: 'fill' }; + } + + // Add new format targets if not already present + if (!logoObj.targets && (logoObj.colorConfig.target || logoObj.colorConfig.selector)) { + logoObj.targets = {}; + + if (logoObj.colorConfig.selector) { + // Split multiple selectors (e.g., "#text, #logo_int") + const selectors = logoObj.colorConfig.selector.split(',').map(s => s.trim()); + + // Create a target for each selector + selectors.forEach((selector, index) => { + logoObj.targets[`selector_${index + 1}`] = selector; + }); + } else if (logoObj.colorConfig.target) { + logoObj.targets.main = logoObj.colorConfig.target; + } else { + logoObj.targets.main = 'path'; + } + } + + // Create sets if there are colors but no sets + if (logoObj.colors && !logoObj.sets) { + logoObj.sets = {}; + let setIndex = 1; + + // Create a set for each color + for (const [colorName, colorValue] of Object.entries(logoObj.colors)) { + const setName = `set_${setIndex}`; + logoObj.sets[setName] = {}; + + // Apply this color to all targets + Object.keys(logoObj.targets || {}).forEach(targetName => { + logoObj.sets[setName][targetName] = colorName; + }); + + setIndex++; + } + } + } } return logos; diff --git a/src/components/Grid.svelte b/src/components/Grid.svelte index 76a86d6..a315381 100644 --- a/src/components/Grid.svelte +++ b/src/components/Grid.svelte @@ -62,6 +62,10 @@ $: getLogoThemeColor = logo => getDefaultLogoColor(logo.colors, theme); path={logo.path} color={logo.colors ? (logo._activeColor || getLogoThemeColor(logo)) : undefined} colorConfig={logo.colors ? logo.colorConfig : undefined} + targets={logo.targets} + sets={logo.sets} + colors={logo.colors} + activeSet={logo._activeSet} alt={logo.name} /> {/key} @@ -105,17 +109,40 @@ $: getLogoThemeColor = logo => getDefaultLogoColor(logo.colors, theme); - {#each logo.colors as colorObj} - logo._activeColor = colorObj.value} - on:keydown|stopPropagation={(e) => (e.key === 'Enter' || e.key === ' ') && (logo._activeColor = colorObj.value)} - > - {/each} + {#if logo.sets} + {#each Object.entries(logo.sets) as [setName, setConfig], i} + { + logo._activeColor = Object.values(logo.colors)[i % Object.keys(logo.colors).length]; + logo._activeSet = setName; + }} + on:keydown|stopPropagation={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + logo._activeColor = Object.values(logo.colors)[i % Object.keys(logo.colors).length]; + logo._activeSet = setName; + } + }} + > + {i + 1} + + {/each} + {:else} + {#each Object.entries(logo.colors) as [colorName, colorValue], i} + logo._activeColor = colorValue} + on:keydown|stopPropagation={(e) => (e.key === 'Enter' || e.key === ' ') && (logo._activeColor = colorValue)} + > + {/each} + {/if} {/if} @@ -189,4 +216,20 @@ $: getLogoThemeColor = logo => getDefaultLogoColor(logo.colors, theme); position: absolute; bottom: 0; } + + .set-circle { + background: var(--color-border); + color: var(--color-text); + font-size: 10px; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + } + + /* Dark theme variation */ + :global(.dark-theme) .set-circle { + background: #444; + color: #eee; + } diff --git a/src/components/InlineSvg.svelte b/src/components/InlineSvg.svelte index d910d59..cafe2b6 100644 --- a/src/components/InlineSvg.svelte +++ b/src/components/InlineSvg.svelte @@ -3,6 +3,10 @@ export let path; export let color; export let colorConfig = { target: "path", attribute: "fill" }; + export let targets = null; + export let sets = null; + export let activeSet = null; + export let colors = null; // Add colors object for access to all color values export const alt = ""; let svgHtml = ""; @@ -54,36 +58,70 @@ }); // Remove all diff --git a/src/components/Preview.svelte b/src/components/Preview.svelte index d70437c..9b545d3 100644 --- a/src/components/Preview.svelte +++ b/src/components/Preview.svelte @@ -141,6 +141,10 @@ path={logo.path} color={logo.colors ? (logo._activeColor || getLogoThemeColor(logo)) : undefined} colorConfig={validColorConfig} + targets={logo.targets} + sets={logo.sets} + colors={logo.colors} + activeSet={logo._activeSet} alt={logo.name} /> {:else} @@ -166,17 +170,40 @@ - {#each logo.colors as colorObj} - logo._activeColor = colorObj.value} - on:keydown|stopPropagation={(e) => (e.key === 'Enter' || e.key === ' ') && (logo._activeColor = colorObj.value)} - > - {/each} + {#if logo.sets} + {#each Object.entries(logo.sets) as [setName, setConfig], i} + { + logo._activeColor = Object.values(logo.colors)[i % Object.keys(logo.colors).length]; + logo._activeSet = setName; + }} + on:keydown|stopPropagation={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + logo._activeColor = Object.values(logo.colors)[i % Object.keys(logo.colors).length]; + logo._activeSet = setName; + } + }} + > + {i + 1} + + {/each} + {:else} + {#each Object.entries(logo.colors) as [colorName, colorValue]} + logo._activeColor = colorValue} + on:keydown|stopPropagation={(e) => (e.key === 'Enter' || e.key === ' ') && (logo._activeColor = colorValue)} + > + {/each} + {/if} {/if} {#if logo.brand} @@ -198,6 +225,21 @@