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 @@