feat: update logos, add logo background

This commit is contained in:
2026-09-05 12:04:05 +05:00
parent af990e1b32
commit 2448815113
3 changed files with 144 additions and 70 deletions
@@ -2,11 +2,27 @@
import { outfit, duckState } from '$lib/duck/state.svelte';
import { capById, teeById, TEE_LOGO } from '$lib/duck/wardrobe';
import { playQuack } from '$lib/duck/quack';
import { logoById, logoBackdrop } from '$lib/duck/logos';
import Logo from './Logo.svelte';
const cap = $derived(capById(outfit.cap));
const tee = $derived(teeById(outfit.tee));
// фактический цвет логотипа и подложка-«стикер», если он сливается с тканью
const teeLogoDef = $derived(logoById(outfit.teeLogo));
const capLogoDef = $derived(logoById(outfit.capLogo));
const capMonoColor = $derived(cap.dark ? '#f8fafc' : '#0f172a');
const teeBackdrop = $derived.by(() => {
if (!tee.img || !teeLogoDef) return null;
const colors = teeLogoDef.mono ? [tee.mono] : teeLogoDef.tints;
return logoBackdrop(tee.color, colors, TEE_LOGO.x, TEE_LOGO.y, TEE_LOGO.s);
});
const capBackdrop = $derived.by(() => {
if (cap.id === 'none' || !capLogoDef) return null;
const colors = capLogoDef.mono ? [capMonoColor] : capLogoDef.tints;
return logoBackdrop(cap.color, colors, cap.logo.x, cap.logo.y, cap.logo.s);
});
let quacking = $state(false);
function poke() {
@@ -31,6 +47,18 @@
{/if}
<svg class="layer" viewBox="0 0 800 920" aria-hidden="true">
{#if outfit.teeLogo && tee.img}
{#if teeBackdrop}
<rect
x={teeBackdrop.x}
y={teeBackdrop.y}
width={teeBackdrop.w}
height={teeBackdrop.h}
rx={teeBackdrop.rx}
fill={teeBackdrop.fill}
stroke={teeBackdrop.stroke}
stroke-width="2"
/>
{/if}
<Logo
id={outfit.teeLogo}
size={TEE_LOGO.s}
@@ -42,12 +70,24 @@
{#if cap.id !== 'none'}
<g class="cap">{@html cap.svg}</g>
{#if outfit.capLogo}
{#if capBackdrop}
<rect
x={capBackdrop.x}
y={capBackdrop.y}
width={capBackdrop.w}
height={capBackdrop.h}
rx={capBackdrop.rx}
fill={capBackdrop.fill}
stroke={capBackdrop.stroke}
stroke-width="2"
/>
{/if}
<Logo
id={outfit.capLogo}
size={cap.logo.s}
x={cap.logo.x - cap.logo.s / 2}
y={cap.logo.y - cap.logo.s / 2}
color={cap.dark ? '#f8fafc' : '#0f172a'}
color={capMonoColor}
/>
{/if}
{/if}
+95 -63
View File
@@ -13,6 +13,9 @@ export interface LogoDef {
category: LogoCategory;
/** mono = рисуется цветом currentColor (подстраивается под тёмную/светлую поверхность) */
mono?: boolean;
/** доминирующие цвета марки (можно несколько) — для авто-подложки,
* когда любой из них сливается с кепкой/футболкой */
tints: string[];
/** разметка под viewBox 0 0 24 24 */
svg: string;
}
@@ -27,142 +30,171 @@ export const LOGOS: LogoDef[] = [
id: 'js',
label: 'JavaScript',
category: 'lang',
tints: ['#f7df1e'],
svg: badge('#f7df1e', 'JS', '#1a1a1a')
},
{
{
id: 'ts',
label: 'TypeScript',
category: 'lang',
tints: ['#3178c6'],
svg: badge('#3178c6', 'TS', '#ffffff')
},
{
{
id: 'go',
label: 'Go',
category: 'lang',
tints: ['#00add8'],
svg: badge('#00add8', 'GO', '#ffffff', 9)
},
{
id: 'rust',
label: 'Rust',
category: 'lang',
svg:
`<g fill="#2b2b2b">` +
[0, 45, 90, 135, 180, 225, 270, 315]
.map((a) => `<rect x="10.9" y="1.4" width="2.2" height="3.6" rx="0.7" transform="rotate(${a} 12 12)"/>`)
.join('') +
`<circle cx="12" cy="12" r="8.4"/></g>` +
`<circle cx="12" cy="12" r="5.4" fill="#f74c00"/>` +
`<text x="12" y="15.6" text-anchor="middle" font-family="system-ui, 'Segoe UI', sans-serif" font-weight="800" font-size="8.5" fill="#ffffff">R</text>`
tints: ['#000000'],
svg: `<path d="M23.8346 11.7033l-1.0073-.6236a13.7268 13.7268 0 00-.0283-.2936l.8656-.8069a.3483.3483 0 00-.1154-.578l-1.1066-.414a8.4958 8.4958 0 00-.087-.2856l.6904-.9587a.3462.3462 0 00-.2257-.5446l-1.1663-.1894a9.3574 9.3574 0 00-.1407-.2622l.49-1.0761a.3437.3437 0 00-.0274-.3361.3486.3486 0 00-.3006-.154l-1.1845.0416a6.7444 6.7444 0 00-.1873-.2268l.2723-1.153a.3472.3472 0 00-.417-.4172l-1.1532.2724a14.0183 14.0183 0 00-.2278-.1873l.0415-1.1845a.3442.3442 0 00-.49-.328l-1.076.491c-.0872-.0476-.1742-.0952-.2623-.1407l-.1903-1.1673A.3483.3483 0 0016.256.955l-.9597.6905a8.4867 8.4867 0 00-.2855-.086l-.414-1.1066a.3483.3483 0 00-.5781-.1154l-.8069.8666a9.2936 9.2936 0 00-.2936-.0284L12.2946.1683a.3462.3462 0 00-.5892 0l-.6236 1.0073a13.7383 13.7383 0 00-.2936.0284L9.9803.3374a.3462.3462 0 00-.578.1154l-.4141 1.1065c-.0962.0274-.1903.0567-.2855.086L7.744.955a.3483.3483 0 00-.5447.2258L7.009 2.348a9.3574 9.3574 0 00-.2622.1407l-1.0762-.491a.3462.3462 0 00-.49.328l.0416 1.1845a7.9826 7.9826 0 00-.2278.1873L3.8413 3.425a.3472.3472 0 00-.4171.4171l.2713 1.1531c-.0628.075-.1255.1509-.1863.2268l-1.1845-.0415a.3462.3462 0 00-.328.49l.491 1.0761a9.167 9.167 0 00-.1407.2622l-1.1662.1894a.3483.3483 0 00-.2258.5446l.6904.9587a13.303 13.303 0 00-.087.2855l-1.1065.414a.3483.3483 0 00-.1155.5781l.8656.807a9.2936 9.2936 0 00-.0283.2935l-1.0073.6236a.3442.3442 0 000 .5892l1.0073.6236c.008.0982.0182.1964.0283.2936l-.8656.8079a.3462.3462 0 00.1155.578l1.1065.4141c.0273.0962.0567.1914.087.2855l-.6904.9587a.3452.3452 0 00.2268.5447l1.1662.1893c.0456.088.0922.1751.1408.2622l-.491 1.0762a.3462.3462 0 00.328.49l1.1834-.0415c.0618.0769.1235.1528.1873.2277l-.2713 1.1541a.3462.3462 0 00.4171.4161l1.153-.2713c.075.0638.151.1255.2279.1863l-.0415 1.1845a.3442.3442 0 00.49.327l1.0761-.49c.087.0486.1741.0951.2622.1407l.1903 1.1662a.3483.3483 0 00.5447.2268l.9587-.6904a9.299 9.299 0 00.2855.087l.414 1.1066a.3452.3452 0 00.5781.1154l.8079-.8656c.0972.0111.1954.0203.2936.0294l.6236 1.0073a.3472.3472 0 00.5892 0l.6236-1.0073c.0982-.0091.1964-.0183.2936-.0294l.8069.8656a.3483.3483 0 00.578-.1154l.4141-1.1066a8.4626 8.4626 0 00.2855-.087l.9587.6904a.3452.3452 0 00.5447-.2268l.1903-1.1662c.088-.0456.1751-.0931.2622-.1407l1.0762.49a.3472.3472 0 00.49-.327l-.0415-1.1845a6.7267 6.7267 0 00.2267-.1863l1.1531.2713a.3472.3472 0 00.4171-.416l-.2713-1.1542c.0628-.0749.1255-.1508.1863-.2278l1.1845.0415a.3442.3442 0 00.328-.49l-.49-1.076c.0475-.0872.0951-.1742.1407-.2623l1.1662-.1893a.3483.3483 0 00.2258-.5447l-.6904-.9587.087-.2855 1.1066-.414a.3462.3462 0 00.1154-.5781l-.8656-.8079c.0101-.0972.0202-.1954.0283-.2936l1.0073-.6236a.3442.3442 0 000-.5892zm-6.7413 8.3551a.7138.7138 0 01.2986-1.396.714.714 0 11-.2997 1.396zm-.3422-2.3142a.649.649 0 00-.7715.5l-.3573 1.6685c-1.1035.501-2.3285.7795-3.6193.7795a8.7368 8.7368 0 01-3.6951-.814l-.3574-1.6684a.648.648 0 00-.7714-.499l-1.473.3158a8.7216 8.7216 0 01-.7613-.898h7.1676c.081 0 .1356-.0141.1356-.088v-2.536c0-.074-.0536-.0881-.1356-.0881h-2.0966v-1.6077h2.2677c.2065 0 1.1065.0587 1.394 1.2088.0901.3533.2875 1.5044.4232 1.8729.1346.413.6833 1.2381 1.2685 1.2381h3.5716a.7492.7492 0 00.1296-.0131 8.7874 8.7874 0 01-.8119.9526zM6.8369 20.024a.714.714 0 11-.2997-1.396.714.714 0 01.2997 1.396zM4.1177 8.9972a.7137.7137 0 11-1.304.5791.7137.7137 0 011.304-.579zm-.8352 1.9813l1.5347-.6824a.65.65 0 00.33-.8585l-.3158-.7147h1.2432v5.6025H3.5669a8.7753 8.7753 0 01-.2834-3.348zm6.7343-.5437V8.7836h2.9601c.153 0 1.0792.1772 1.0792.8697 0 .575-.7107.7815-1.2948.7815zm10.7574 1.4862c0 .2187-.008.4363-.0243.651h-.9c-.09 0-.1265.0586-.1265.1477v.413c0 .973-.5487 1.1846-1.0296 1.2382-.4576.0517-.9648-.1913-1.0275-.4717-.2704-1.5186-.7198-1.8436-1.4305-2.4034.8817-.5599 1.799-1.386 1.799-2.4915 0-1.1936-.819-1.9458-1.3769-2.3153-.7825-.5163-1.6491-.6195-1.883-.6195H5.4682a8.7651 8.7651 0 014.907-2.7699l1.0974 1.151a.648.648 0 00.9182.0213l1.227-1.1743a8.7753 8.7753 0 016.0044 4.2762l-.8403 1.8982a.652.652 0 00.33.8585l1.6178.7188c.0283.2875.0425.577.0425.8717zm-9.3006-9.5993a.7128.7128 0 11.984 1.0316.7137.7137 0 01-.984-1.0316zm8.3389 6.71a.7107.7107 0 01.9395-.3625.7137.7137 0 11-.9405.3635z"/>`
},
{
id: 'python',
label: 'Python',
category: 'lang',
tints: ['#387eb8', '#ffe052'],
svg:
`<path d="M11.9 2.2c-3.5 0-3.1 1.5-3.1 1.5l0 1.6h3.2v.5H6.6c0 0-2.1-.2-2.1 3.1 0 3.3 1.8 3.2 1.8 3.2h1.1v-1.7c0 0-.1-1.8 1.8-1.8h3.1c0 0 1.8 0 1.8-1.7V4c0-.1.3-1.8-2.2-1.8zM10.4 4a.7.7 0 1 1 0 1.4.7.7 0 0 1 0-1.4z" fill="#4584b6"/>` +
`<path d="M11.9 2.2c-3.5 0-3.1 1.5-3.1 1.5l0 1.6h3.2v.5H6.6c0 0-2.1-.2-2.1 3.1 0 3.3 1.8 3.2 1.8 3.2h1.1v-1.7c0 0-.1-1.8 1.8-1.8h3.1c0 0 1.8 0 1.8-1.7V4c0-.1.3-1.8-2.2-1.8zM10.4 4a.7.7 0 1 1 0 1.4.7.7 0 0 1 0-1.4z" fill="#ffde57" transform="rotate(180 12 12)"/>`
`<defs><linearGradient id="pyGradA" gradientUnits="userSpaceOnUse" x1="63.8159" y1="56.6829" x2="118.4934" y2="1.8225" gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)"> <stop offset="0" stop-color="#387eb8"/> <stop offset="1" stop-color="#366994"/></linearGradient><linearGradient id="pyGradB" gradientUnits="userSpaceOnUse" x1="97.0444" y1="21.6321" x2="155.6665" y2="-34.5308" gradientTransform="matrix(1 0 0 -1 -53.2974 66.4321)"> <stop offset="0" stop-color="#ffe052"/> <stop offset="1" stop-color="#ffc331"/></linearGradient></defs>` +
`<g transform="scale(0.2175)"><path fill="url(#pyGradA)" d="M55.023-0.077c-25.971,0-26.25,10.081-26.25,12.156c0,3.148,0,12.594,0,12.594h26.75v3.781 c0,0-27.852,0-37.375,0c-7.949,0-17.938,4.833-17.938,26.25c0,19.673,7.792,27.281,15.656,27.281c2.335,0,9.344,0,9.344,0 s0-9.765,0-13.125c0-5.491,2.721-15.656,15.406-15.656c15.91,0,19.971,0,26.531,0c3.902,0,14.906-1.696,14.906-14.406 c0-13.452,0-17.89,0-24.219C82.054,11.426,81.515-0.077,55.023-0.077z M40.273,8.392c2.662,0,4.813,2.15,4.813,4.813 c0,2.661-2.151,4.813-4.813,4.813s-4.813-2.151-4.813-4.813C35.46,10.542,37.611,8.392,40.273,8.392z"/><path fill="url(#pyGradB)" d="M55.397,109.923c25.959,0,26.282-10.271,26.282-12.156c0-3.148,0-12.594,0-12.594H54.897v-3.781 c0,0,28.032,0,37.375,0c8.009,0,17.938-4.954,17.938-26.25c0-23.322-10.538-27.281-15.656-27.281c-2.336,0-9.344,0-9.344,0 s0,10.216,0,13.125c0,5.491-2.631,15.656-15.406,15.656c-15.91,0-19.476,0-26.532,0c-3.892,0-14.906,1.896-14.906,14.406 c0,14.475,0,18.265,0,24.219C28.366,100.497,31.562,109.923,55.397,109.923z M70.148,101.454c-2.662,0-4.813-2.151-4.813-4.813 s2.15-4.813,4.813-4.813c2.661,0,4.813,2.151,4.813,4.813S72.809,101.454,70.148,101.454z"/></g>`
},
// --- технологии ---
{
id: 'nginx',
label: 'NGINX',
category: 'tech',
tints: ['#009639'],
svg: badge('#009639', 'n', '#ffffff', 13)
},
{
id: 'kafka',
label: 'Kafka',
category: 'tech',
mono: true,
svg:
`<g fill="currentColor">` +
`<circle cx="12" cy="12" r="3.1"/>` +
`<circle cx="12" cy="3.6" r="2.1"/>` +
`<circle cx="19.1" cy="16.1" r="2.1"/>` +
`<circle cx="4.9" cy="16.1" r="2.1"/>` +
`</g>` +
`<path d="M12 6v2.9M14.5 13.5l2.7 1.5M9.5 13.5l-2.7 1.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" fill="none"/>`
tints: ['#231F20'],
svg: `<path d="M9.71 2.136a1.43 1.43 0 0 0-2.047 0h-.007a1.48 1.48 0 0 0-.421 1.042c0 .41.161.777.422 1.039l.007.007c.257.264.616.426 1.019.426.404 0 .766-.162 1.027-.426l.003-.007c.261-.262.421-.629.421-1.039 0-.408-.159-.777-.421-1.042H9.71zM8.683 22.295c.404 0 .766-.167 1.027-.429l.003-.008c.261-.261.421-.631.421-1.036 0-.41-.159-.778-.421-1.044H9.71a1.42 1.42 0 0 0-1.027-.432 1.4 1.4 0 0 0-1.02.432h-.007c-.26.266-.422.634-.422 1.044 0 .406.161.775.422 1.036l.007.008c.258.262.617.429 1.02.429zm7.89-4.462c.359-.096.683-.33.882-.684l.027-.052a1.47 1.47 0 0 0 .114-1.067 1.454 1.454 0 0 0-.675-.896l-.021-.014a1.425 1.425 0 0 0-1.078-.132c-.36.091-.684.335-.881.686-.2.349-.241.75-.146 1.119.099.363.33.691.675.896h.002c.346.203.737.239 1.101.144zm-6.405-7.342a2.083 2.083 0 0 0-1.485-.627c-.58 0-1.103.242-1.482.627-.378.385-.612.916-.612 1.507s.233 1.124.612 1.514a2.08 2.08 0 0 0 2.967 0c.379-.39.612-.923.612-1.514s-.233-1.122-.612-1.507zm-.835-2.51c.843.141 1.6.552 2.178 1.144h.004c.092.093.182.196.265.299l1.446-.851a3.176 3.176 0 0 1-.047-1.808 3.149 3.149 0 0 1 1.456-1.926l.025-.016a3.062 3.062 0 0 1 2.345-.306c.77.21 1.465.721 1.898 1.482v.002c.431.757.518 1.626.313 2.408a3.145 3.145 0 0 1-1.456 1.928l-.198.118h-.02a3.095 3.095 0 0 1-2.154.201 3.127 3.127 0 0 1-1.514-.944l-1.444.848a4.162 4.162 0 0 1 0 2.879l1.444.846c.413-.47.939-.789 1.514-.944a3.041 3.041 0 0 1 2.371.319l.048.023v.002a3.17 3.17 0 0 1 1.408 1.906 3.215 3.215 0 0 1-.313 2.405l-.026.053-.003-.005a3.147 3.147 0 0 1-1.867 1.436 3.096 3.096 0 0 1-2.371-.318v-.006a3.156 3.156 0 0 1-1.456-1.927 3.175 3.175 0 0 1 .047-1.805l-1.446-.848a3.905 3.905 0 0 1-.265.294l-.004.005a3.938 3.938 0 0 1-2.178 1.138v1.699a3.09 3.09 0 0 1 1.56.862l.002.004c.565.572.914 1.368.914 2.243 0 .873-.35 1.664-.914 2.239l-.002.009a3.1 3.1 0 0 1-2.21.931 3.1 3.1 0 0 1-2.206-.93h-.002v-.009a3.186 3.186 0 0 1-.916-2.239c0-.875.35-1.672.916-2.243v-.004h.002a3.1 3.1 0 0 1 1.558-.862v-1.699a3.926 3.926 0 0 1-2.176-1.138l-.006-.005a4.098 4.098 0 0 1-1.173-2.874c0-1.122.452-2.136 1.173-2.872h.006a3.947 3.947 0 0 1 2.176-1.144V6.289a3.137 3.137 0 0 1-1.558-.864h-.002v-.004a3.192 3.192 0 0 1-.916-2.243c0-.871.35-1.669.916-2.243l.002-.002A3.084 3.084 0 0 1 8.683 0c.861 0 1.641.355 2.21.932v.002h.002c.565.574.914 1.372.914 2.243 0 .876-.35 1.667-.914 2.243l-.002.005a3.142 3.142 0 0 1-1.56.864v1.692zm8.121-1.129l-.012-.019a1.452 1.452 0 0 0-.87-.668 1.43 1.43 0 0 0-1.103.146h.002c-.347.2-.58.529-.677.896-.095.365-.054.768.146 1.119l.007.009c.2.347.519.579.874.673.357.103.755.059 1.098-.144l.019-.009a1.47 1.47 0 0 0 .657-.885 1.493 1.493 0 0 0-.141-1.118"/>`
},
{
id: 'docker',
label: 'Docker',
category: 'tech',
svg:
`<g fill="#2496ed">` +
`<rect x="4.6" y="10" width="3.4" height="3.5" rx="0.5"/>` +
`<rect x="8.6" y="10" width="3.4" height="3.5" rx="0.5"/>` +
`<rect x="12.6" y="10" width="3.4" height="3.5" rx="0.5"/>` +
`<rect x="8.6" y="6.2" width="3.4" height="3.5" rx="0.5"/>` +
`<rect x="12.6" y="6.2" width="3.4" height="3.5" rx="0.5"/>` +
`<path d="M2 13.5h20c0 4.4-3.4 7.6-9 7.9-5.4.3-10-2.6-11-7.9z"/>` +
`</g>`
tints: ['#2496ED'],
svg: `<path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"/>`
},
{
id: 'kubernetes',
label: 'Kubernetes',
category: 'tech',
svg:
`<polygon points="12,2 19.8,5.8 21.7,14.2 16.3,21 7.7,21 2.3,14.2 4.2,5.8" fill="#326ce5"/>` +
`<g stroke="#ffffff" stroke-width="1.3">` +
`<line x1="12" y1="12" x2="12" y2="7.4"/><line x1="12" y1="12" x2="15.6" y2="9.1"/>` +
`<line x1="12" y1="12" x2="16.5" y2="13"/><line x1="12" y1="12" x2="14" y2="16.1"/>` +
`<line x1="12" y1="12" x2="10" y2="16.1"/><line x1="12" y1="12" x2="7.5" y2="13"/>` +
`<line x1="12" y1="12" x2="8.4" y2="9.1"/>` +
`</g>` +
`<circle cx="12" cy="12" r="1.5" fill="#ffffff"/>` +
`<circle cx="12" cy="12" r="4.6" fill="none" stroke="#ffffff" stroke-width="1.5"/>`
tints: ['#326CE5'],
svg: `<path d="M10.204 14.35l.007.01-.999 2.413a5.171 5.171 0 0 1-2.075-2.597l2.578-.437.004.005a.44.44 0 0 1 .484.606zm-.833-2.129a.44.44 0 0 0 .173-.756l.002-.011L7.585 9.7a5.143 5.143 0 0 0-.73 3.255l2.514-.725.002-.009zm1.145-1.98a.44.44 0 0 0 .699-.337l.01-.005.15-2.62a5.144 5.144 0 0 0-3.01 1.442l2.147 1.523.004-.002zm.76 2.75l.723.349.722-.347.18-.78-.5-.623h-.804l-.5.623.179.779zm1.5-3.095a.44.44 0 0 0 .7.336l.008.003 2.134-1.513a5.188 5.188 0 0 0-2.992-1.442l.148 2.615.002.001zm10.876 5.97l-5.773 7.181a1.6 1.6 0 0 1-1.248.594l-9.261.003a1.6 1.6 0 0 1-1.247-.596l-5.776-7.18a1.583 1.583 0 0 1-.307-1.34L2.1 5.573c.108-.47.425-.864.863-1.073L11.305.513a1.606 1.606 0 0 1 1.385 0l8.345 3.985c.438.209.755.604.863 1.073l2.062 8.955c.108.47-.005.963-.308 1.34zm-3.289-2.057c-.042-.01-.103-.026-.145-.034-.174-.033-.315-.025-.479-.038-.35-.037-.638-.067-.895-.148-.105-.04-.18-.165-.216-.216l-.201-.059a6.45 6.45 0 0 0-.105-2.332 6.465 6.465 0 0 0-.936-2.163c.052-.047.15-.133.177-.159.008-.09.001-.183.094-.282.197-.185.444-.338.743-.522.142-.084.273-.137.415-.242.032-.024.076-.062.11-.089.24-.191.295-.52.123-.736-.172-.216-.506-.236-.745-.045-.034.027-.08.062-.111.088-.134.116-.217.23-.33.35-.246.25-.45.458-.673.609-.097.056-.239.037-.303.033l-.19.135a6.545 6.545 0 0 0-4.146-2.003l-.012-.223c-.065-.062-.143-.115-.163-.25-.022-.268.015-.557.057-.905.023-.163.061-.298.068-.475.001-.04-.001-.099-.001-.142 0-.306-.224-.555-.5-.555-.275 0-.499.249-.499.555l.001.014c0 .041-.002.092 0 .128.006.177.044.312.067.475.042.348.078.637.056.906a.545.545 0 0 1-.162.258l-.012.211a6.424 6.424 0 0 0-4.166 2.003 8.373 8.373 0 0 1-.18-.128c-.09.012-.18.04-.297-.029-.223-.15-.427-.358-.673-.608-.113-.12-.195-.234-.329-.349-.03-.026-.077-.062-.111-.088a.594.594 0 0 0-.348-.132.481.481 0 0 0-.398.176c-.172.216-.117.546.123.737l.007.005.104.083c.142.105.272.159.414.242.299.185.546.338.743.522.076.082.09.226.1.288l.16.143a6.462 6.462 0 0 0-1.02 4.506l-.208.06c-.055.072-.133.184-.215.217-.257.081-.546.11-.895.147-.164.014-.305.006-.48.039-.037.007-.09.02-.133.03l-.004.002-.007.002c-.295.071-.484.342-.423.608.061.267.349.429.645.365l.007-.001.01-.003.129-.029c.17-.046.294-.113.448-.172.33-.118.604-.217.87-.256.112-.009.23.069.288.101l.217-.037a6.5 6.5 0 0 0 2.88 3.596l-.09.218c.033.084.069.199.044.282-.097.252-.263.517-.452.813-.091.136-.185.242-.268.399-.02.037-.045.095-.064.134-.128.275-.034.591.213.71.248.12.556-.007.69-.282v-.002c.02-.039.046-.09.062-.127.07-.162.094-.301.144-.458.132-.332.205-.68.387-.897.05-.06.13-.082.215-.105l.113-.205a6.453 6.453 0 0 0 4.609.012l.106.192c.086.028.18.042.256.155.136.232.229.507.342.84.05.156.074.295.145.457.016.037.043.09.062.129.133.276.442.402.69.282.247-.118.341-.435.213-.71-.02-.039-.045-.096-.065-.134-.083-.156-.177-.261-.268-.398-.19-.296-.346-.541-.443-.793-.04-.13.007-.21.038-.294-.018-.022-.059-.144-.083-.202a6.499 6.499 0 0 0 2.88-3.622c.064.01.176.03.213.038.075-.05.144-.114.28-.104.266.039.54.138.87.256.154.06.277.128.448.173.036.01.088.019.13.028l.009.003.007.001c.297.064.584-.098.645-.365.06-.266-.128-.537-.423-.608zM16.4 9.701l-1.95 1.746v.005a.44.44 0 0 0 .173.757l.003.01 2.526.728a5.199 5.199 0 0 0-.108-1.674A5.208 5.208 0 0 0 16.4 9.7zm-4.013 5.325a.437.437 0 0 0-.404-.232.44.44 0 0 0-.372.233h-.002l-1.268 2.292a5.164 5.164 0 0 0 3.326.003l-1.27-2.296h-.01zm1.888-1.293a.44.44 0 0 0-.27.036.44.44 0 0 0-.214.572l-.003.004 1.01 2.438a5.15 5.15 0 0 0 2.081-2.615l-2.6-.44-.004.005z"/>`
},
{
id: 'redis',
label: 'Redis',
category: 'tech',
svg:
`<g><path d="M12 6 L21 10.2 12 14.4 3 10.2 Z" fill="#9e2a20" transform="translate(0 6.4)"/>` +
`<path d="M12 6 L21 10.2 12 14.4 3 10.2 Z" fill="#c93325" transform="translate(0 3.2)"/>` +
`<path d="M12 6 L21 10.2 12 14.4 3 10.2 Z" fill="#ee4a2e"/></g>`
tints: ['#FF4438'],
svg: `<path d="M22.71 13.145c-1.66 2.092-3.452 4.483-7.038 4.483-3.203 0-4.397-2.825-4.48-5.12.701 1.484 2.073 2.685 4.214 2.63 4.117-.133 6.94-3.852 6.94-7.239 0-4.05-3.022-6.972-8.268-6.972-3.752 0-8.4 1.428-11.455 3.685C2.59 6.937 3.885 9.958 4.35 9.626c2.648-1.904 4.748-3.13 6.784-3.744C8.12 9.244.886 17.05 0 18.425c.1 1.261 1.66 4.648 2.424 4.648.232 0 .431-.133.664-.365a100.49 100.49 0 0 0 5.54-6.765c.222 3.104 1.748 6.898 6.014 6.898 3.819 0 7.604-2.756 9.33-8.965.2-.764-.73-1.361-1.261-.73zm-4.349-5.013c0 1.959-1.926 2.922-3.685 2.922-.941 0-1.664-.247-2.235-.568 1.051-1.592 2.092-3.225 3.21-4.973 1.972.334 2.71 1.43 2.71 2.619z"/>`
},
// --- организации ---
// --- организации ---,
{
id: 'github',
label: 'GitHub',
category: 'org',
mono: true,
svg:
`<g transform="scale(1.5)" fill="currentColor">` +
`<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>` +
`</g>`
tints: ['#181717'],
svg: `<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>`
},
{
id: 'gitlab',
label: 'GitLab',
category: 'org',
svg:
`<path d="M12 21.3 L3.7 15.3 C3.4 15.1 3.3 14.7 3.4 14.4 L5.2 5.2 C5.3 4.8 5.8 4.7 6 5.1 L8.8 9.9 H15.2 L18 5.1 C18.2 4.7 18.7 4.8 18.8 5.2 L20.6 14.4 C20.7 14.7 20.6 15.1 20.3 15.3 Z" fill="#fc6d26"/>`
tints: ['#FC6D26'],
svg: `<path d="m23.6004 9.5927-.0337-.0862L20.3.9814a.851.851 0 0 0-.3362-.405.8748.8748 0 0 0-.9997.0539.8748.8748 0 0 0-.29.4399l-2.2055 6.748H7.5375l-2.2057-6.748a.8573.8573 0 0 0-.29-.4412.8748.8748 0 0 0-.9997-.0537.8585.8585 0 0 0-.3362.4049L.4332 9.5015l-.0325.0862a6.0657 6.0657 0 0 0 2.0119 7.0105l.0113.0087.03.0213 4.976 3.7264 2.462 1.8633 1.4995 1.1321a1.0085 1.0085 0 0 0 1.2197 0l1.4995-1.1321 2.4619-1.8633 5.006-3.7489.0125-.01a6.0682 6.0682 0 0 0 2.0094-7.003z"/>`
},
{
id: 'bitbucket',
label: 'Bitbucket',
category: 'org',
svg:
`<path d="M3.4 3.8h17.2c.5 0 .9.5.8 1L18.9 19c-.1.5-.5.8-1 .8H6.1c-.5 0-.9-.3-1-.8L2.6 4.8c-.1-.5.3-1 .8-1z" fill="#2684ff"/>` +
`<path d="M10.2 9.5h3.7l-.7 4.6h-2.3z" fill="#ffffff"/>`
tints: ['#0052CC'],
svg: `<path d="M.778 1.213a.768.768 0 00-.768.892l3.263 19.81c.084.5.515.868 1.022.873H19.95a.772.772 0 00.77-.646l3.27-20.03a.768.768 0 00-.768-.891zM14.52 15.53H9.522L8.17 8.466h7.561z"/>`
},
{
id: 'git',
label: 'Git',
category: 'org',
svg:
`<rect x="4.4" y="4.4" width="15.2" height="15.2" rx="2.4" transform="rotate(45 12 12)" fill="#f05032"/>` +
`<g stroke="#ffffff" stroke-width="1.4" fill="none" stroke-linecap="round">` +
`<path d="M9.2 10.8v.4c0 2.5 2.3 2.2 4.3 2.6"/>` +
`</g>` +
`<circle cx="9.2" cy="9.4" r="1.6" fill="#ffffff"/>` +
`<circle cx="15" cy="14.6" r="1.6" fill="#ffffff"/>`
tints: ['#F03C2E'],
svg: `<path d="M13.09 23.549a1.54 1.54 0 0 1-2.18 0L.451 13.089a1.54 1.54 0 0 1 0-2.179l7.191-7.19 2.733 2.733a1.85 1.85 0 0 0 .964 2.326v6.66a1.849 1.849 0 1 0 1.54 0V8.957l2.508 2.508a1.85 1.85 0 1 0 1.09-1.09l-2.634-2.634a1.85 1.85 0 0 0-2.378-2.377L8.73 2.63 10.91.451a1.54 1.54 0 0 1 2.179 0l10.459 10.46a1.54 1.54 0 0 1 0 2.179z"/>`
}
];
];;
const byId = new Map(LOGOS.map((l) => [l.id, l]));
export function logoById(id: string | null | undefined): LogoDef | undefined {
return id ? byId.get(id) : undefined;
}
// ─── авто-подложка под логотип ───────────────────────────────────────────────
// Если цвет логотипа близок к цвету ткани (кепки/футболки), под логотип
// рисуется контрастная плашка-«стикер». Порог подобран на глаз: ниже —
// подложка появляется чаще, выше — реже.
const CONTRAST_THRESHOLD = 120; // 0…441 (евклидово расстояние в RGB)
function hexToRgb(hex: string): [number, number, number] {
const h = hex.replace('#', '');
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
}
function luminance(rgb: [number, number, number]): number {
return (0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]) / 255;
}
function colorDistance(a: string, b: string): number {
const ra = hexToRgb(a);
const rb = hexToRgb(b);
return Math.hypot(ra[0] - rb[0], ra[1] - rb[1], ra[2] - rb[2]);
}
export interface LogoBackdrop {
x: number;
y: number;
w: number;
h: number;
rx: number;
fill: string;
stroke: string;
}
/**
* Плашка под логотип, если он сливается с поверхностью.
* surface — цвет ткани, logoColor — фактический цвет логотипа
* (для mono-логотипов это подставленный currentColor), cx/cy/size — центр
* и сторона квадрата логотипа на канвасе утки.
*/
export function logoBackdrop(
surface: string | null | undefined,
logoColors: string[],
cx: number,
cy: number,
size: number
): LogoBackdrop | null {
if (!surface || !surface.startsWith('#')) return null;
const colors = logoColors.filter((c) => c.startsWith('#'));
if (!colors.length) return null;
if (colors.every((c) => colorDistance(surface, c) >= CONTRAST_THRESHOLD)) return null;
// белая плашка на цветной/тёмной ткани, тёмная — на почти белой
const light = luminance(hexToRgb(surface)) > 0.68;
const pad = size * 0.14;
return {
x: cx - size / 2 - pad,
y: cy - size / 2 - pad,
w: size + pad * 2,
h: size + pad * 2,
rx: size * 0.22,
fill: light ? '#0f172a' : '#f8fafc',
stroke: light ? 'rgba(0,0,0,0.3)' : 'rgba(15,23,42,0.18)'
};
}
+8 -6
View File
@@ -12,6 +12,8 @@ export interface TeeDef {
label: string;
/** PNG-слой футболки (перекрашен из TShirt.jfif), null = без футболки */
img: string | null;
/** цвет ткани (примерный) — для авто-подложки под логотип */
color: string;
dark: boolean;
/** цвет для монохромных логотипов */
mono: string;
@@ -178,12 +180,12 @@ export interface CapDef extends CapShape {
export const CAPS: readonly CapDef[] = CAP_LIST;
const TEE_LIST = [
{ id: 'none', label: 'Без футболки', img: null, dark: false, mono: '' },
{ id: 'white', label: 'Белая', img: '/duck/tee-white.png', dark: false, mono: '#0f172a' },
{ id: 'black', label: 'Чёрная', img: '/duck/tee-black.png', dark: true, mono: '#f8fafc' },
{ id: 'green', label: 'Терминальная', img: '/duck/tee-green.png', dark: false, mono: '#0f172a' },
{ id: 'orange', label: 'Оранжевая', img: '/duck/tee-orange.png', dark: false, mono: '#0f172a' },
{ id: 'blue', label: 'Голубая', img: '/duck/tee-blue.png', dark: false, mono: '#0f172a' }
{ id: 'none', label: 'Без футболки', img: null, color: '', dark: false, mono: '' },
{ id: 'white', label: 'Белая', img: '/duck/tee-white.png', color: '#e9edf1', dark: false, mono: '#0f172a' },
{ id: 'black', label: 'Чёрная', img: '/duck/tee-black.png', color: '#2b2f33', dark: true, mono: '#f8fafc' },
{ id: 'green', label: 'Терминальная', img: '/duck/tee-green.png', color: '#16a34a', dark: false, mono: '#0f172a' },
{ id: 'orange', label: 'Оранжевая', img: '/duck/tee-orange.png', color: '#f97316', dark: false, mono: '#0f172a' },
{ id: 'blue', label: 'Голубая', img: '/duck/tee-blue.png', color: '#2563eb', dark: false, mono: '#0f172a' }
] as const;
/** id всех футболок — выводится из TEE_LIST, отдельно не правится */