feat(icons): derive every icon from nous-girl SVGs + platform backgrounds
The icon pipeline now composes all 35 targets from two source axes instead of a single hand-edited master: - girl art: assets/nous-girl-black.svg / nous-girl-white.svg (brand kit) - backgrounds: assets/backgrounds/ (squircle light/dark, logo frames, BrandMark tiles) assets/icon-master*.svg are generated artifacts (squircle background + girl nested in the 824px HIG content safe zone). New dark-appearance artifacts (icon-dark.* containers, appx *-dark logos, logo-dark wordmark, nous-logo-dark) land everywhere a surface consumes them: docusaurus navbar srcDark, BrandMark dark tile (keyed off renderedMode). nous-girl.jpg jpgs are replaced by lossless 8-bit palette PNGs saved with compress_level=0 (stored deflate blocks, byte-identical across hosts for the CI freshness lane). Includes the temp linux-truth harvest step in icons-freshness-check.yml so the committed compressed bytes can be refreshed from the ubuntu regen.
No files matched your search
@@ -1,10 +1,12 @@
|
||||
name: Icon Assets Freshness
|
||||
|
||||
# Guard for the single-master icon pipeline. assets/icon-master.svg is the
|
||||
# source of truth for all 23 generated icons (see scripts/generate_icons.py);
|
||||
# this check regenerates every target and byte-compares against the committed
|
||||
# files. The repo can never hold a hand-edited or stale generated icon, and a
|
||||
# master change can never land without its regenerated outputs.
|
||||
# Guard for the icon pipeline. The sources of truth are the nous-girl SVGs
|
||||
# (assets/nous-girl-*.svg) and the per-platform backgrounds
|
||||
# (assets/backgrounds/*.svg); assets/icon-master*.svg and every raster target
|
||||
# are generated from those (see scripts/generate_icons.py). This check
|
||||
# regenerates everything and byte-compares against the committed files. The
|
||||
# repo can never hold a hand-edited or stale generated icon, and an art or
|
||||
# background change can never land without its regenerated outputs.
|
||||
#
|
||||
# Byte-determinism note: resvg renders the same SVG to identical bytes on any
|
||||
# host for pure-shape artwork. If the master ever contains <text>, font
|
||||
@@ -43,3 +45,23 @@ jobs:
|
||||
|
||||
- name: Verify generated icons are in sync with the master
|
||||
run: uv run python scripts/generate_icons.py --check
|
||||
continue-on-error: true
|
||||
|
||||
# TEMP: upload linux-truth regeneration so the committed set can be
|
||||
# refreshed from the host CI actually checks against.
|
||||
- name: Regenerate + upload (temp artifact harvest)
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
uv run python scripts/generate_icons.py
|
||||
tar czf icons-linux.tgz \
|
||||
apps/bootstrap-installer/public/ \
|
||||
apps/bootstrap-installer/src-tauri/icons/ \
|
||||
apps/desktop/assets/ \
|
||||
apps/desktop/public/ \
|
||||
web/public/favicon.ico website/static/img/
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: icons-linux
|
||||
path: icons-linux.tgz
|
||||
|
Before Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 607 B After Width: | Height: | Size: 918 B |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 27 KiB |
@@ -2,12 +2,13 @@ import { cn } from '../lib/utils'
|
||||
|
||||
const assetPath = (path: string) => `${import.meta.env.BASE_URL}${path.replace(/^\/+/, '')}`
|
||||
|
||||
// Brand badge: nous-girl mark on a white tile, identical in light/dark.
|
||||
// Brand badge: nous-girl mark on a white tile (the light BrandMark variant).
|
||||
// Ported from apps/desktop's BrandMark; asset lives in this app's public/.
|
||||
// The installer has no theme system, so it always uses the light asset.
|
||||
export function BrandMark({ className, ...props }: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span className={cn('inline-flex size-14 shrink-0 items-center justify-center bg-white', className)} {...props}>
|
||||
<img alt="" className="size-full object-contain" src={assetPath('nous-girl.jpg')} />
|
||||
<img alt="" className="size-full object-contain" src={assetPath('nous-girl.png')} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -99,8 +99,9 @@ for call-site shadow or border inventions.
|
||||
| `--chrome-action-hover` | hover fill for quiet controls |
|
||||
| `--theme-primary`, `--ui-accent` | brand/accent |
|
||||
|
||||
Never hardcode `border-gray-*`, `bg-white`, `text-black`, etc. The white tile in
|
||||
`BrandMark` is the one sanctioned literal (the mark needs a fixed backdrop).
|
||||
Never hardcode `border-gray-*`, `bg-white`, `text-black`, etc. The two tiles in
|
||||
`BrandMark` (white in light mode, `#0d1117` in dark) are the sanctioned literals
|
||||
(the mark needs a fixed backdrop).
|
||||
|
||||
## Buttons — one component
|
||||
|
||||
@@ -253,7 +254,11 @@ Sizes: `default`, `xs`, `overlay` (titlebar glyph counts).
|
||||
action. Do not introduce a third icon set or mix styles within one control
|
||||
group.
|
||||
- **`BrandMark`** (`src/components/brand-mark.tsx`) is the brand glyph — the
|
||||
`nous-girl` mark on a white tile, softly rounded, identical in light/dark.
|
||||
`nous-girl` mark on a fixed tile, theme-aware: black girl on white in light
|
||||
mode, white girl on the `#0d1117` dark tile in dark mode (keyed off
|
||||
`renderedMode` so bright surfaces in dark skins stay on the light mark).
|
||||
The marks are generated by `scripts/generate_icons.py` from the girl SVGs
|
||||
+ tile backgrounds (`assets/nous-girl-*.svg` × `assets/backgrounds/tile-*.svg`).
|
||||
It replaced scattered Sparkles glyphs in updates / onboarding / about. Use it
|
||||
for hero/brand moments; don't reintroduce decorative star/sparkle icons.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 856 B After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 64 KiB |
@@ -1,19 +1,25 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useTheme } from '@/themes'
|
||||
|
||||
const assetPath = (path: string) => `${import.meta.env.BASE_URL}${path.replace(/^\/+/, '')}`
|
||||
|
||||
// Brand badge: nous-girl mark on a white tile, identical in light/dark.
|
||||
// Brand badge: nous-girl mark on a fixed tile, theme-aware — light mode shows
|
||||
// the black girl on white, dark mode the white girl on the nous dark surface.
|
||||
// Fills the tile (softly rounded); size via className (default size-14).
|
||||
export function BrandMark({ className, ...props }: React.ComponentProps<'span'>) {
|
||||
const { renderedMode } = useTheme()
|
||||
const dark = renderedMode === 'dark'
|
||||
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
'inline-flex size-14 shrink-0 items-center justify-center overflow-hidden rounded-md bg-white',
|
||||
dark && 'bg-[#0d1117]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<img alt="" className="size-full object-contain" src={assetPath('nous-girl.jpg')} />
|
||||
<img alt="" className="size-full object-contain" src={assetPath(dark ? 'nous-girl-dark.png' : 'nous-girl.png')} />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1772 1799">
|
||||
<!-- website wordmark background, dark: white frame (L/R 14, T 17, B 16) + #0d1117 interior -->
|
||||
<rect x="0" y="0" width="1772" height="1799" rx="2" fill="#ffffff"/>
|
||||
<rect x="14" y="17" width="1743" height="1766" fill="#0d1117"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1772 1799">
|
||||
<!-- website wordmark background, light: black frame (L/R 14, T 17, B 16) + white interior -->
|
||||
<rect x="0" y="0" width="1772" height="1799" rx="2" fill="#000000"/>
|
||||
<rect x="14" y="17" width="1743" height="1766" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 306 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- app-icon background, dark: #0d1117 rounded square (rx=245), transparent corners -->
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="245" fill="#0d1117"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 236 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- app-icon background, light: white rounded square (rx=245), transparent corners -->
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="245" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 235 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
|
||||
<!-- BrandMark tile, dark: solid #0d1117 -->
|
||||
<rect x="0" y="0" width="256" height="256" fill="#0d1117"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 179 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
|
||||
<!-- BrandMark tile, light: solid white -->
|
||||
<rect x="0" y="0" width="256" height="256" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 178 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- app-icon background, dark: #0d1117 rounded square (rx=245), transparent corners -->
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="245" fill="#0d1117"/>
|
||||
<svg x="100" y="100" width="824" height="824" viewBox="117.8860869140625 96.45225292968749 5026.234069335937 5390.609247070312">
|
||||
<path
|
||||
id="rect179"
|
||||
style="display:inline;opacity:1;fill:#ffffff;stroke-width:50"
|
||||
d="M 2727.8496 100.02344 C 2712.1357 100.02344 2634.4158 101.29576 2609.7832 104.69336 C 2577.9307 108.51567 2546.0792 112.33831 2526.543 114.03711 C 2491.7175 117.01001 2388.0908 133.57372 2360.0605 139.51953 L 2358.7852 139.51953 C 2353.6888 140.79363 2341.3738 143.34222 2332.4551 145.04102 C 2331.181 145.46572 2330.3299 145.46484 2329.4805 145.46484 C 2323.9593 146.73894 2315.8909 148.43879 2309.0957 149.71289 C 2303.5746 150.98699 2298.4786 151.83615 2294.6562 152.68555 C 2285.3128 154.80905 2270.8718 157.78167 2261.9531 159.48047 C 2216.5102 168.39918 2129.4468 189.21045 2107.7871 195.15625 C 2105.2389 196.00566 2102.691 196.85352 2100.9922 196.85352 C 2094.197 197.27822 2086.128 198.97739 2074.2363 202.375 C 2073.3869 202.375 2072.1131 203.22461 2071.2637 203.22461 C 2069.5649 203.64931 2064.043 205.34787 2060.2207 206.19727 C 2056.3984 207.04667 2052.5762 207.89581 2048.7539 209.16992 C 2025.8201 215.11572 2018.1763 217.2402 2004.5859 221.0625 C 2001.1883 221.91191 1997.3662 222.76106 1993.1191 224.03516 L 1992.2695 224.03516 C 1987.1731 225.30926 1982.076 227.00715 1977.4043 228.28125 C 1969.7597 230.40475 1965.5128 231.2552 1962.1152 232.5293 C 1939.606 238.89981 1918.797 245.69478 1891.1914 255.46289 C 1870.8058 262.2581 1858.4896 266.08029 1855.9414 266.92969 C 1854.2426 267.3544 1852.5426 267.77951 1850.8438 268.62891 C 1849.9943 268.62891 1847.8718 269.47764 1846.5977 269.90234 C 1842.7754 271.17644 1839.3779 272.45159 1835.1309 274.15039 C 1832.5827 274.99979 1826.6357 277.12347 1822.3887 278.82227 C 1818.9911 280.52108 1815.17 281.79426 1811.3477 283.06836 C 1790.5373 290.28827 1782.0417 293.68617 1776.9453 296.23438 C 1772.2736 297.93318 1760.3826 302.18011 1755.7109 303.87891 C 1752.3133 305.15301 1734.0515 311.52253 1725.1328 315.76953 C 1717.0635 319.16714 1685.6351 332.75773 1667.373 340.40234 C 1546.758 389.66759 1324.6407 503.48803 1257.1133 550.20508 C 1255.8392 551.05448 1251.591 553.60305 1248.1934 555.72656 C 1240.9735 560.39826 1230.7807 566.76925 1220.5879 573.56445 C 1191.2836 591.82657 1139.0463 624.95314 1112.7148 644.91406 C 1109.3172 647.46227 1100.8237 653.8315 1086.8086 663.59961 C 1067.2724 677.19002 976.38541 745.14305 955.15039 762.98047 C 890.59592 815.64332 847.70129 854.29111 792.49023 910.35156 C 755.11659 948.1499 701.17922 1007.6068 683.3418 1032.6641 C 682.4924 1033.5135 681.64432 1034.7884 680.79492 1036.0625 C 677.39731 1040.3095 671.87658 1047.9539 666.35547 1055.1738 C 665.93077 1055.1738 665.50586 1056.0234 665.50586 1056.0234 C 642.99674 1087.0265 600.52562 1151.5803 577.5918 1192.3516 C 539.36876 1258.6048 466.74537 1405.5521 451.88086 1450.5703 C 451.45616 1451.8444 450.60742 1453.1174 450.60742 1453.9668 C 445.08631 1462.4608 440.83855 1472.654 439.56445 1483.2715 C 438.29035 1486.6691 436.16626 1493.039 433.19336 1500.6836 C 432.76866 1501.533 432.34375 1501.9592 432.34375 1502.8086 C 425.54854 1521.9201 409.83584 1568.2114 397.51953 1609.832 C 392.84783 1622.5731 386.47727 1648.0547 381.38086 1668.0156 C 377.55856 1682.4554 373.31059 1701.1431 369.48828 1718.9805 C 364.81658 1741.4896 363.11806 1750.8335 362.69336 1756.7793 C 358.44636 1769.5204 356.32401 1786.0833 355.47461 1791.1797 C 354.62521 1799.6737 351.22768 1822.607 347.83008 1844.6914 C 342.73367 1882.4897 341.88281 1890.1351 341.88281 1990.7891 C 341.88281 2091.4431 342.3071 2108.0065 352.5 2166.6152 L 352.5 2167.0391 C 361.41871 2216.3043 372.88719 2257.0767 390.72461 2303.3691 L 390.72461 2304.6426 C 391.99871 2307.6155 393.27257 2310.5883 393.69727 2312.2871 C 395.82077 2319.9318 399.21696 2327.1507 403.88867 2336.4941 C 408.13567 2345.4128 413.65837 2357.305 418.33008 2366.6484 C 437.4416 2404.4467 448.05819 2422.2853 466.32031 2450.7402 C 483.30833 2477.0717 521.9561 2519.1169 549.98633 2545.873 C 565.37302 2561.6647 585.3918 2580.9301 594.95312 2587.8438 C 595.78656 2588.5837 596.9367 2589.3677 598.40234 2590.4668 C 602.64935 2593.8644 608.17139 2598.1104 614.11719 2602.3574 C 660.83424 2639.3064 751.29459 2690.696 805.65625 2705.5605 C 806.50565 2705.9852 807.78059 2706.834 809.05469 2706.834 C 813.7264 2708.5328 821.36972 2711.0816 827.74023 2713.2051 C 828.16493 2713.6298 829.01475 2713.6309 829.43945 2713.6309 C 878.28 2729.7695 899.09036 2734.0158 913.10547 2734.8652 C 910.98197 2756.9496 908.0098 2784.9806 904.1875 2821.0801 L 904.1875 2821.9297 C 903.3381 2831.6978 902.48699 2843.1638 901.21289 2853.7812 C 899.51409 2872.0433 897.8162 2889.4567 896.9668 2900.0742 C 896.9668 2904.3212 896.11719 2908.5685 896.11719 2913.2402 C 894.84309 2927.2553 893.5701 2943.3949 892.7207 2952.7383 L 891.87109 2959.5332 C 890.59699 2975.2471 888.04841 3001.1539 886.34961 3023.2383 C 885.92491 3027.4853 885.07617 3031.7306 885.07617 3036.4023 C 883.37737 3056.7879 880.82771 3081.8453 879.12891 3092.4629 L 879.12891 3095.0117 C 877.43011 3110.3009 875.73242 3131.1119 875.73242 3141.3047 C 875.73242 3145.127 874.03366 3160.4154 871.91016 3177.8281 L 871.91016 3179.1035 C 865.53965 3236.0134 8Line truncated
|
||||
</svg>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 134 KiB |
@@ -1,4 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- app-icon background, light: white rounded square (rx=245), transparent corners -->
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="245" fill="#ffffff"/>
|
||||
<circle cx="512" cy="512" r="420" fill="#e34c4c"/>
|
||||
<svg x="100" y="100" width="824" height="824" viewBox="182.1875888671875 150.036837890625 4951.215650390624 5337.024662109375">
|
||||
<path
|
||||
id="path14"
|
||||
style="opacity:1;fill:#000000"
|
||||
d="M 2780.0999 2572.1925 C 2731.3999 2572.7925 2683.4019 2574.5933 2673.3019 2576.2933 C 2641.3019 2580.3933 2609.7995 2583.9939 2590.1995 2585.7939 C 2558.6995 2588.6939 2461.4006 2604.1934 2436.0006 2609.4934 C 2430.6006 2610.6934 2417.6007 2613.5942 2407.5007 2615.3942 C 2396.9007 2617.7942 2381.4 2620.6937 2373.1 2622.4937 C 2364.3 2624.2937 2350.0005 2627.1938 2341.1005 2628.9938 C 2297.8005 2637.7938 2215.4009 2657.4932 2195.8009 2662.7932 C 2189.3009 2664.5932 2181.499 2666.2947 2179.199 2666.2947 C 2176.899 2666.2947 2169.8014 2668.0943 2163.8014 2669.7943 C 2158.5014 2671.5943 2146.5999 2674.4938 2138.1999 2676.8938 C 2110.2999 2683.9938 2106.2018 2685.2929 2084.8018 2691.1929 C 2072.9018 2694.6929 2059.8019 2698.2929 2056.3019 2699.4929 C 2034.9019 2705.3929 2015.3015 2711.9934 1989.8015 2720.8934 C 1973.8015 2726.1934 1957.1997 2731.5932 1952.9997 2732.7932 C 1948.2997 2734.5932 1942.3011 2736.2934 1939.9011 2737.4934 C 1937.0011 2738.6934 1925.1005 2742.8921 1913.8005 2746.9921 C 1901.9005 2751.0921 1888.8006 2755.7914 1885.3006 2757.6914 C 1881.1006 2759.4914 1869.9003 2763.5918 1860.3003 2767.0918 C 1850.3003 2770.5918 1839.0002 2774.8924 1835.4002 2776.5924 C 1827.7002 2780.0924 1796.302 2793.7927 1777.302 2801.5927 C 1661.602 2848.9927 1452.2004 2956.3923 1391.6004 2998.4923 C 1387.4004 3001.3923 1370.9005 3011.4918 1354.8005 3022.1918 C 1312.0005 3048.8918 1272.9013 3074.3922 1253.9013 3088.6922 C 1249.2013 3092.1922 1237.2994 3100.5915 1227.1994 3107.6915 C 1207.5994 3121.3915 1120.4013 3186.4936 1103.8013 3200.7936 C 1042.1013 3251.1936 1002.2989 3287.3931 950.69897 3339.5931 C 911.59897 3379.3931 864.10036 3432.7915 851.00036 3451.1915 C 849.20036 3453.5915 841.50097 3464.2936 833.80097 3474.2936 C 813.60097 3501.5936 773.80044 3562.0926 752.50044 3599.9926 C 713.90044 3666.9926 647.50056 3803.4919 635.60056 3839.0919 C 632.70056 3847.8919 629.00156 3855.6932 627.80156 3856.2932 C 626.60156 3857.4932 625.99975 3859.7927 625.99975 3861.5927 C 625.99975 3863.3927 621.90105 3876.992 616.50105 3890.692 C 609.40105 3910.892 594.60095 3954.7915 583.30095 3992.6915 C 580.90095 3998.5915 572.69995 4030.0928 568.99995 4044.8928 C 561.79995 4073.3928 552.3999 4117.2925 552.3999 4122.0925 C 552.3999 4126.8925 551.20065 4130.4923 550.00065 4133.9923 C 548.80065 4136.8923 546.50045 4147.6925 545.30045 4157.0925 C 544.10045 4166.4925 540.59977 4190.2931 537.59977 4209.8931 C 532.89977 4242.4931 532.30212 4253.6937 532.30212 4342.7937 C 532.30212 4431.8937 532.89873 4451.2934 541.69873 4502.8934 C 549.49873 4546.1934 559.50006 4582.3942 575.50006 4623.8942 C 578.40006 4630.9942 580.80149 4638.1924 580.80149 4638.6924 C 580.80149 4639.8924 589.70013 4658.2925 600.40013 4680.1925 C 617.00013 4713.9925 625.99914 4728.8923 643.19914 4755.5923 C 655.09914 4773.9923 687.60031 4810.7922 715.60031 4837.4922 C 733.40031 4855.2922 750.00089 4870.6928 751.80089 4871.8928 C 753.60089 4873.0928 762.50077 4880.2942 772.00077 4887.2942 C 815.80077 4922.2942 897.69957 4966.7939 938.09957 4977.3939 C 941.59957 4978.5939 952.89964 4982.0941 962.99964 4985.7941 C 1000.3996 4998.2941 1026.501 5005.3935 1031.801 5002.9935 C 1034.201 5002.3935 1033.6004 5001.1932 1030.6004 5000.0932 C 1017.5004 4994.6932 987.80012 4972.1933 970.60012 4954.9933 C 960.60012 4943.6933 945.70125 4925.8918 938.00125 4914.6918 C 923.20125 4892.7918 892.90185 4838.7923 885.80185 4820.9923 C 883.40185 4815.0923 879.3008 4805.5921 877.0008 4799.6921 C 874.6008 4793.7921 871.70069 4786.5945 870.50069 4784.2945 C 867.60069 4778.3945 857.9995 4751.0939 851.4995 4731.4939 C 846.1995 4716.6939 845.59958 4707.0929 849.69958 4707.0929 C 851.49958 4706.4929 857.39972 4717.0931 864.49972 4730.1931 C 892.39972 4784.7931 936.9016 4841.7937 987.9016 4887.9937 C 1020.5016 4917.6937 1072.1013 4958.5931 1078.0013 4958.5931 C 1083.9013 4958.5931 1082.0991 4960.9939 1085.1991 4964.4939 L 1091.6992 4969.7934 L 1089.9011 4999.4939 C 1088.7011 5028.5939 1085.1995 5067.6918 1076.1995 5153.1918 C 1073.7995 5178.7918 1070.3 5212.5939 1069.1 5227.8939 C 1067.9 5243.2939 1066.2009 5266.9933 1064.9009 5280.0933 C 1063.7009 5293.0933 1060.8001 5323.3922 1059.0001 5347.6922 C 1056.6001 5371.9922 1053.7013 5403.9928 1051.3013 5418.8928 C 1049.5013 5433.6928 1048.401 5450.8931 1048.401 5456.7931 C 1048.401 5462.6931 1046.6021 5481.7921 1044.3021 5499.5921 C 1038.4021 5553.5921 1034.1997 5592.0928 1031.1997 5620.5928 C 1027.6997 5658.4928 1022.3018 5708.9931 1019.3018 5728.4931 C 1016.9018 5748.6931 1010.5013 5801.9917 1008.0013 5828.1917 C 1004.5013 5861.9917 998.50199 5911.893 997.30199 5915.393 C 996.10199 5917.193 994.90048 5927.8919 993.80048 5939.6919 C 989.70048 5974.6919 986.70083 5997.1924 982.00083 6031.0924 C 979.10083 6048.8924 976.10138 6070.7934 974.90138 6080.3934 C 973.70138 6090.4934 972.00176 6099.993 971.40176 6102.293 C 970.80176 6104.093 969.00025 6113.5928 967.90025 6122.4928 C 965.50025 6138.4928 963.20171 6150.994 954.80171 6194.894 C 952.40171 6206.Line truncated
|
||||
transform="matrix(1.0330354,0,0,1.0330354,-145.41428,-2499.328)" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 113 KiB |
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
version="1.1"
|
||||
viewBox="0 0 5487.0615 5487.0615"
|
||||
sodipodi:docname="Nous Girl Without Frame.svg"
|
||||
width="5487.0615"
|
||||
height="5487.0615"
|
||||
inkscape:version="1.4.4 (dcaf3e7, 2026-05-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs10">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath10">
|
||||
<rect
|
||||
style="fill:#000000"
|
||||
id="rect11"
|
||||
width="5600"
|
||||
height="5600"
|
||||
x="0"
|
||||
y="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath12">
|
||||
<rect
|
||||
style="opacity:0.42;fill:#00ff00;stroke-width:0.993322"
|
||||
id="rect13"
|
||||
width="5525.4526"
|
||||
height="5600"
|
||||
x="0"
|
||||
y="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:antialias-rendering="true"
|
||||
inkscape:clip-to-page="false"
|
||||
labelstyle="default"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:zoom="0.0625"
|
||||
inkscape:cx="5240"
|
||||
inkscape:cy="4864"
|
||||
inkscape:window-width="1635"
|
||||
inkscape:window-height="1020"
|
||||
inkscape:window-x="-6"
|
||||
inkscape:window-y="-6"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1">
|
||||
<inkscape:grid
|
||||
id="grid10"
|
||||
units="px"
|
||||
originx="-56.469222"
|
||||
originy="-56.469315"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="false" />
|
||||
<sodipodi:guide
|
||||
position="189.61933,4064.4693"
|
||||
orientation="1,0"
|
||||
id="guide17"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="5132.0393,3472.4693"
|
||||
orientation="1,0"
|
||||
id="guide18"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="2726.527,5461.2663"
|
||||
orientation="0,-1"
|
||||
id="guide19"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="4711.3214,112.93857"
|
||||
orientation="0,-1"
|
||||
id="guide20"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<!-- Generator: Adobe Illustrator 30.0.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 123) -->
|
||||
<path
|
||||
id="path14"
|
||||
style="opacity:1;fill:#000000"
|
||||
inkscape:label="nous girl"
|
||||
d="M 2780.0999 2572.1925 C 2731.3999 2572.7925 2683.4019 2574.5933 2673.3019 2576.2933 C 2641.3019 2580.3933 2609.7995 2583.9939 2590.1995 2585.7939 C 2558.6995 2588.6939 2461.4006 2604.1934 2436.0006 2609.4934 C 2430.6006 2610.6934 2417.6007 2613.5942 2407.5007 2615.3942 C 2396.9007 2617.7942 2381.4 2620.6937 2373.1 2622.4937 C 2364.3 2624.2937 2350.0005 2627.1938 2341.1005 2628.9938 C 2297.8005 2637.7938 2215.4009 2657.4932 2195.8009 2662.7932 C 2189.3009 2664.5932 2181.499 2666.2947 2179.199 2666.2947 C 2176.899 2666.2947 2169.8014 2668.0943 2163.8014 2669.7943 C 2158.5014 2671.5943 2146.5999 2674.4938 2138.1999 2676.8938 C 2110.2999 2683.9938 2106.2018 2685.2929 2084.8018 2691.1929 C 2072.9018 2694.6929 2059.8019 2698.2929 2056.3019 2699.4929 C 2034.9019 2705.3929 2015.3015 2711.9934 1989.8015 2720.8934 C 1973.8015 2726.1934 1957.1997 2731.5932 1952.9997 2732.7932 C 1948.2997 2734.5932 1942.3011 2736.2934 1939.9011 2737.4934 C 1937.0011 2738.6934 1925.1005 2742.8921 1913.8005 2746.9921 C 1901.9005 2751.0921 1888.8006 2755.7914 1885.3006 2757.6914 C 1881.1006 2759.4914 1869.9003 2763.5918 1860.3003 2767.0918 C 1850.3003 2770.5918 1839.0002 2774.8924 1835.4002 2776.5924 C 1827.7002 2780.0924 1796.302 2793.7927 1777.302 2801.5927 C 1661.602 2848.9927 1452.2004 2956.3923 1391.6004 2998.4923 C 1387.4004 3001.3923 1370.9005 3011.4918 1354.8005 3022.1918 C 1312.0005 3048.8918 1272.9013 3074.3922 1253.9013 3088.6922 C 1249.2013 3092.1922 1237.2994 3100.5915 1227.1994 3107.6915 C 1207.5994 3121.3915 1120.4013 3186.4936 1103.8013 3200.7936 C 1042.1013 3251.1936 1002.2989 3287.3931 950.69897 3339.5931 C 911.59897 3379.3931 864.10036 3432.7915 851.00036 3451.1915 C 849.20036 3453.5915 841.50097 3464.2936 833.80097 3474.2936 C 813.60097 3501.5936 773.80044 3562.0926 752.50044 3599.9926 C 713.90044 3666.9926 647.50056 3803.4919 635.60056 3839.0919 C 632.70056 3847.8919 629.00156 3855.6932 627.80156 3856.2932 C 626.60156 3857.4932 625.99975 3859.7927 625.99975 3861.5927 C 625.99975 3863.3927 621.90105 3876.992 616.50105 3890.692 C 609.40105 3910.892 594.60095 3954.7915 583.30095 3992.6915 C 580.90095 3998.5915 572.69995 4030.0928 568.99995 4044.8928 C 561.79995 4073.3928 552.3999 4117.2925 552.3999 4122.0925 C 552.3999 4126.8925 551.20065 4130.4923 550.00065 4133.9923 C 548.80065 4136.8923 546.50045 4147.6925 545.30045 4157.0925 C 544.10045 4166.4925 540.59977 4190.2931 537.59977 4209.8931 C 532.89977 4242.4931 532.30212 4253.6937 532.30212 4342.7937 C 532.30212 4431.8937 532.89873 4451.2934 541.69873 4502.8934 C 549.49873 4546.1934 559.50006 4582.3942 575.50006 4623.8942 C 578.40006 4630.9942 580.80149 4638.1924 580.80149 4638.6924 C 580.80149 4639.8924 589.70013 4658.2925 600.40013 4680.1925 C 617.00013 4713.9925 625.99914 4728.8923 643.19914 4755.5923 C 655.09914 4773.9923 687.60031 4810.7922 715.60031 4837.4922 C 733.40031 4855.2922 750.00089 4870.6928 751.80089 4871.8928 C 753.60089 4873.0928 762.50077 4880.2942 772.00077 4887.2942 C 815.80077 4922.2942 897.69957 4966.7939 938.09957 4977.3939 C 941.59957 4978.5939 952.89964 4982.0941 962.99964 4985.7941 C 1000.3996 4998.2941 1026.501 5005.3935 1031.801 5002.9935 C 1034.201 5002.3935 1033.6004 5001.1932 1030.6004 5000.0932 C 1017.5004 4994.6932 987.80012 4972.1933 970.60012 4954.9933 C 960.60012 4943.6933 945.70125 4925.8918 938.00125 4914.6918 C 923.20125 4892.7918 892.90185 4838.7923 885.80185 4820.9923 C 883.40185 4815.0923 879.3008 4805.5921 877.0008 4799.6921 C 874.6008 4793.7921 871.70069 4786.5945 870.50069 4784.2945 C 867.60069 4778.3945 857.9995 4751.0939 851.4995 4731.4939 C 846.1995 4716.6939 845.59958 4707.0929 849.69958 4707.0929 C 851.49958 4706.4929 857.39972 4717.0931 864.49972 4730.1931 C 892.39972 4784.7931 936.9016 4841.7937 987.9016 4887.9937 C 1020.5016 4917.6937 1072.1013 4958.5931 1078.0013 4958.5931 C 1083.9013 4958.5931 1082.0991 4960.9939 1085.1991 4964.4939 L 1091.6992 4969.7934 L 1089.9011 4999.4939 C 1088.7011 5028.5939 1085.1995 5067.6918 1076.1995 5153.1918 C 1073.7995 5178.7918 1070.3 5212.5939 1069.1 5227.8939 C 1067.9 5243.2939 1066.2009 5266.9933 1064.9009 5280.0933 C 1063.7009 5293.0933 1060.8001 5323.3922 1059.0001 5347.6922 C 1056.6001 5371.9922 1053.7013 5403.9928 1051.3013 5418.8928 C 1049.5013 5433.6928 1048.401 5450.8931 1048.401 5456.7931 C 1048.401 5462.6931 1046.6021 5481.7921 1044.3021 5499.5921 C 1038.4021 5553.5921 1034.1997 5592.0928 1031.1997 5620.5928 C 1027.6997 5658.4928 1022.3018 5708.9931 1019.3018 5728.4931 C 1016.9018 5748.6931 1010.5013 5801.9917 1008.0013 5828.1917 C 1004.5013 5861.9917 998.50199 5911.893 997.30199 5915.393 C 996.10199 5917.193 994.90048 5927.8919 993.80048 5939.6919 C 989.70048 5974.6919 986.70083 5997.1924 982.00083 6031.0924 C 979.10083 6048.8924 976.10138 6070.7934 974.90138 6080.3934 C 973.70138 6090.4934 972.00176 6099.993 971.40176 6102.293 C 970.80176 6104.093 969.00025 6113.5928 967.90025 6122.4928 C 965.50025 6138.4928 963.20171 6150.994 954.80171 6194.894 C 952.40171 6206.Line truncated
|
||||
transform="matrix(1.0330354,0,0,1.0330354,-145.41428,-2499.328)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 115 KiB |
@@ -0,0 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Layer_1"
|
||||
version="1.1"
|
||||
viewBox="0 0 5487.0615 5558.8086"
|
||||
sodipodi:docname="Nous Girl White.svg"
|
||||
width="5487.0615"
|
||||
height="5558.8086"
|
||||
inkscape:version="1.4.4 (dcaf3e7, 2026-05-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs10">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath10">
|
||||
<rect
|
||||
style="fill:#000000"
|
||||
id="rect11"
|
||||
width="5600"
|
||||
height="5600"
|
||||
x="0"
|
||||
y="0" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath12">
|
||||
<rect
|
||||
style="opacity:0.42;fill:#00ff00;stroke-width:0.993322"
|
||||
id="rect13"
|
||||
width="5525.4526"
|
||||
height="5600"
|
||||
x="0"
|
||||
y="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:antialias-rendering="true"
|
||||
inkscape:clip-to-page="false"
|
||||
labelstyle="default"
|
||||
showgrid="false"
|
||||
showguides="true"
|
||||
inkscape:zoom="0.125"
|
||||
inkscape:cx="792"
|
||||
inkscape:cy="3304"
|
||||
inkscape:window-width="1635"
|
||||
inkscape:window-height="1020"
|
||||
inkscape:window-x="-6"
|
||||
inkscape:window-y="-6"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1">
|
||||
<inkscape:grid
|
||||
id="grid10"
|
||||
units="px"
|
||||
originx="-56.469223"
|
||||
originy="-56.469315"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="false" />
|
||||
<sodipodi:guide
|
||||
position="189.61933,4064.4693"
|
||||
orientation="1,0"
|
||||
id="guide17"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="5132.0393,3472.4693"
|
||||
orientation="1,0"
|
||||
id="guide18"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="2726.527,5461.2663"
|
||||
orientation="0,-1"
|
||||
id="guide19"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="4711.3214,112.93857"
|
||||
orientation="0,-1"
|
||||
id="guide20"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="982.34964,2852.417"
|
||||
orientation="1,0"
|
||||
id="guide1"
|
||||
inkscape:locked="false" />
|
||||
<sodipodi:guide
|
||||
position="2804.3855,-71.74701"
|
||||
orientation="0,-1"
|
||||
id="guide179"
|
||||
inkscape:locked="false" />
|
||||
</sodipodi:namedview>
|
||||
<!-- Generator: Adobe Illustrator 30.0.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 123) -->
|
||||
<path
|
||||
id="rect179"
|
||||
style="display:inline;opacity:1;fill:#ffffff;stroke-width:50"
|
||||
d="M 2727.8496 100.02344 C 2712.1357 100.02344 2634.4158 101.29576 2609.7832 104.69336 C 2577.9307 108.51567 2546.0792 112.33831 2526.543 114.03711 C 2491.7175 117.01001 2388.0908 133.57372 2360.0605 139.51953 L 2358.7852 139.51953 C 2353.6888 140.79363 2341.3738 143.34222 2332.4551 145.04102 C 2331.181 145.46572 2330.3299 145.46484 2329.4805 145.46484 C 2323.9593 146.73894 2315.8909 148.43879 2309.0957 149.71289 C 2303.5746 150.98699 2298.4786 151.83615 2294.6562 152.68555 C 2285.3128 154.80905 2270.8718 157.78167 2261.9531 159.48047 C 2216.5102 168.39918 2129.4468 189.21045 2107.7871 195.15625 C 2105.2389 196.00566 2102.691 196.85352 2100.9922 196.85352 C 2094.197 197.27822 2086.128 198.97739 2074.2363 202.375 C 2073.3869 202.375 2072.1131 203.22461 2071.2637 203.22461 C 2069.5649 203.64931 2064.043 205.34787 2060.2207 206.19727 C 2056.3984 207.04667 2052.5762 207.89581 2048.7539 209.16992 C 2025.8201 215.11572 2018.1763 217.2402 2004.5859 221.0625 C 2001.1883 221.91191 1997.3662 222.76106 1993.1191 224.03516 L 1992.2695 224.03516 C 1987.1731 225.30926 1982.076 227.00715 1977.4043 228.28125 C 1969.7597 230.40475 1965.5128 231.2552 1962.1152 232.5293 C 1939.606 238.89981 1918.797 245.69478 1891.1914 255.46289 C 1870.8058 262.2581 1858.4896 266.08029 1855.9414 266.92969 C 1854.2426 267.3544 1852.5426 267.77951 1850.8438 268.62891 C 1849.9943 268.62891 1847.8718 269.47764 1846.5977 269.90234 C 1842.7754 271.17644 1839.3779 272.45159 1835.1309 274.15039 C 1832.5827 274.99979 1826.6357 277.12347 1822.3887 278.82227 C 1818.9911 280.52108 1815.17 281.79426 1811.3477 283.06836 C 1790.5373 290.28827 1782.0417 293.68617 1776.9453 296.23438 C 1772.2736 297.93318 1760.3826 302.18011 1755.7109 303.87891 C 1752.3133 305.15301 1734.0515 311.52253 1725.1328 315.76953 C 1717.0635 319.16714 1685.6351 332.75773 1667.373 340.40234 C 1546.758 389.66759 1324.6407 503.48803 1257.1133 550.20508 C 1255.8392 551.05448 1251.591 553.60305 1248.1934 555.72656 C 1240.9735 560.39826 1230.7807 566.76925 1220.5879 573.56445 C 1191.2836 591.82657 1139.0463 624.95314 1112.7148 644.91406 C 1109.3172 647.46227 1100.8237 653.8315 1086.8086 663.59961 C 1067.2724 677.19002 976.38541 745.14305 955.15039 762.98047 C 890.59592 815.64332 847.70129 854.29111 792.49023 910.35156 C 755.11659 948.1499 701.17922 1007.6068 683.3418 1032.6641 C 682.4924 1033.5135 681.64432 1034.7884 680.79492 1036.0625 C 677.39731 1040.3095 671.87658 1047.9539 666.35547 1055.1738 C 665.93077 1055.1738 665.50586 1056.0234 665.50586 1056.0234 C 642.99674 1087.0265 600.52562 1151.5803 577.5918 1192.3516 C 539.36876 1258.6048 466.74537 1405.5521 451.88086 1450.5703 C 451.45616 1451.8444 450.60742 1453.1174 450.60742 1453.9668 C 445.08631 1462.4608 440.83855 1472.654 439.56445 1483.2715 C 438.29035 1486.6691 436.16626 1493.039 433.19336 1500.6836 C 432.76866 1501.533 432.34375 1501.9592 432.34375 1502.8086 C 425.54854 1521.9201 409.83584 1568.2114 397.51953 1609.832 C 392.84783 1622.5731 386.47727 1648.0547 381.38086 1668.0156 C 377.55856 1682.4554 373.31059 1701.1431 369.48828 1718.9805 C 364.81658 1741.4896 363.11806 1750.8335 362.69336 1756.7793 C 358.44636 1769.5204 356.32401 1786.0833 355.47461 1791.1797 C 354.62521 1799.6737 351.22768 1822.607 347.83008 1844.6914 C 342.73367 1882.4897 341.88281 1890.1351 341.88281 1990.7891 C 341.88281 2091.4431 342.3071 2108.0065 352.5 2166.6152 L 352.5 2167.0391 C 361.41871 2216.3043 372.88719 2257.0767 390.72461 2303.3691 L 390.72461 2304.6426 C 391.99871 2307.6155 393.27257 2310.5883 393.69727 2312.2871 C 395.82077 2319.9318 399.21696 2327.1507 403.88867 2336.4941 C 408.13567 2345.4128 413.65837 2357.305 418.33008 2366.6484 C 437.4416 2404.4467 448.05819 2422.2853 466.32031 2450.7402 C 483.30833 2477.0717 521.9561 2519.1169 549.98633 2545.873 C 565.37302 2561.6647 585.3918 2580.9301 594.95312 2587.8438 C 595.78656 2588.5837 596.9367 2589.3677 598.40234 2590.4668 C 602.64935 2593.8644 608.17139 2598.1104 614.11719 2602.3574 C 660.83424 2639.3064 751.29459 2690.696 805.65625 2705.5605 C 806.50565 2705.9852 807.78059 2706.834 809.05469 2706.834 C 813.7264 2708.5328 821.36972 2711.0816 827.74023 2713.2051 C 828.16493 2713.6298 829.01475 2713.6309 829.43945 2713.6309 C 878.28 2729.7695 899.09036 2734.0158 913.10547 2734.8652 C 910.98197 2756.9496 908.0098 2784.9806 904.1875 2821.0801 L 904.1875 2821.9297 C 903.3381 2831.6978 902.48699 2843.1638 901.21289 2853.7812 C 899.51409 2872.0433 897.8162 2889.4567 896.9668 2900.0742 C 896.9668 2904.3212 896.11719 2908.5685 896.11719 2913.2402 C 894.84309 2927.2553 893.5701 2943.3949 892.7207 2952.7383 L 891.87109 2959.5332 C 890.59699 2975.2471 888.04841 3001.1539 886.34961 3023.2383 C 885.92491 3027.4853 885.07617 3031.7306 885.07617 3036.4023 C 883.37737 3056.7879 880.82771 3081.8453 879.12891 3092.4629 L 879.12891 3095.0117 C 877.43011 3110.3009 875.73242 3131.1119 875.73242 3141.3047 C 875.73242 3145.127 874.03366 3160.4154 871.91016 3177.8281 L 871.91016 3179.1035 C 865.53965 3236.0134 8Line truncated
|
||||
inkscape:label="nous girl" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 136 KiB |
@@ -1,17 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate every app icon in the repo from a single master SVG.
|
||||
"""Generate every app icon in the repo from the nous-girl art + platform backgrounds.
|
||||
|
||||
Usage (from repo root):
|
||||
.venv/Scripts/python.exe scripts/generate_icons.py # write
|
||||
.venv/Scripts/python.exe scripts/generate_icons.py --check # verify only
|
||||
|
||||
Master: assets/icon-master.svg
|
||||
A 1024x1024 SVG. The background must be a WHITE ROUNDED SQUARE
|
||||
(rx ~ 245, spanning the full canvas) with the artwork on top —
|
||||
the transparent corners outside the squircle are what give the
|
||||
icons their app-icon shape everywhere downstream. If the master
|
||||
is missing, a red-circle placeholder is written automatically;
|
||||
drop in the real artwork and re-run.
|
||||
Sources of truth — two axes, composed per target:
|
||||
Girl art (vector): assets/nous-girl-black.svg (black positive space)
|
||||
assets/nous-girl-white.svg (white positive space)
|
||||
straight from the Nous brand kit (Inkscape exports,
|
||||
5487^2 viewBox, one path each).
|
||||
|
||||
Backgrounds (per platform surface, light/dark):
|
||||
assets/backgrounds/squircle-light.svg white rounded
|
||||
assets/backgrounds/squircle-dark.svg #0d1117 rounded
|
||||
assets/backgrounds/logo-frame.svg black frame +
|
||||
white interior (website)
|
||||
assets/backgrounds/logo-frame-dark.svg white frame +
|
||||
#0d1117 interior
|
||||
assets/backgrounds/tile-light.svg solid white (BrandMark)
|
||||
assets/backgrounds/tile-dark.svg solid #0d1117
|
||||
|
||||
The master SVGs (assets/icon-master.svg light, assets/icon-master-dark.svg
|
||||
dark) are GENERATED artifacts — squircle background + girl nested into the
|
||||
824px HIG content safe zone — not hand-edited sources. The 1024 master is
|
||||
what every squircle target renders from.
|
||||
|
||||
If the girl SVGs are missing a red-circle placeholder master is written so a
|
||||
partial checkout still generates; --check then flags the drift.
|
||||
|
||||
--check mode regenerates every target in memory and byte-compares it
|
||||
against the committed file, exiting nonzero on any drift. This is what
|
||||
@@ -25,36 +41,48 @@ Deps:
|
||||
Pillow (core dependency), resvg-py (dev extra):
|
||||
uv sync --extra dev
|
||||
|
||||
Outputs (23 files, all measured against the originals):
|
||||
apps/desktop/assets/icon.png 1024x1024 squircle
|
||||
apps/desktop/assets/icon.ico 16,24,32,48,64,128,256
|
||||
apps/desktop/assets/icon.icns 16..1024 (real ICNS)
|
||||
apps/desktop/assets/appx/Wide310x150Logo.png 310x150, squircle 100 centered
|
||||
apps/desktop/assets/appx/StoreLogo.png 50x50 squircle
|
||||
apps/desktop/assets/appx/Square44x44Logo.png 44x44 squircle
|
||||
apps/desktop/assets/appx/Square150x150Logo.png 150x150 squircle
|
||||
apps/desktop/public/apple-touch-icon.png 1024x1024 squircle
|
||||
apps/desktop/public/nous-girl.jpg 256x256 JPEG on white
|
||||
Outputs (35 files):
|
||||
assets/icon-master.svg generated light master
|
||||
assets/icon-master-dark.svg generated dark master
|
||||
apps/desktop/assets/icon.png 1024x1024 squircle (light)
|
||||
apps/desktop/assets/icon.ico 16,24,32,48,64,128,256
|
||||
apps/desktop/assets/icon.icns 16..1024 (real ICNS)
|
||||
apps/desktop/assets/icon-dark.png 1024x1024 squircle (dark)
|
||||
apps/desktop/assets/icon-dark.ico 16,24,32,48,64,128,256
|
||||
apps/desktop/assets/icon-dark.icns 16..1024 (real ICNS)
|
||||
apps/desktop/assets/appx/Wide310x150Logo.png 310x150, squircle 100 centered
|
||||
apps/desktop/assets/appx/StoreLogo.png 50x50 squircle
|
||||
apps/desktop/assets/appx/Square44x44Logo.png 44x44 squircle
|
||||
apps/desktop/assets/appx/Square150x150Logo.png 150x150 squircle
|
||||
apps/desktop/assets/appx/*-dark.png dark-appearance logos (ready
|
||||
to wire into the MSIX
|
||||
manifest when app-builder-lib
|
||||
supports contrast images)
|
||||
apps/desktop/public/apple-touch-icon.png 1024x1024 squircle
|
||||
apps/desktop/public/nous-girl.png 256x256 PNG, black girl on white (light)
|
||||
apps/desktop/public/nous-girl-dark.png 256x256 PNG, white girl on #0d1117 (dark)
|
||||
apps/bootstrap-installer/src-tauri/icons/32x32.png 32x32
|
||||
apps/bootstrap-installer/src-tauri/icons/128x128.png 128x128
|
||||
apps/bootstrap-installer/src-tauri/icons/128x128@2x.png 256x256
|
||||
apps/bootstrap-installer/src-tauri/icons/icon.ico 16,32,64,128,256
|
||||
apps/bootstrap-installer/src-tauri/icons/icon.icns 16..1024
|
||||
apps/bootstrap-installer/public/nous-girl.jpg 256x256 JPEG on white
|
||||
apps/bootstrap-installer/public/nous-girl.png 256x256 PNG, black girl on white (light)
|
||||
website/static/img/logo.png 1772x1799 black-frame wordmark
|
||||
website/static/img/logo-dark.png 1772x1799 white-frame wordmark
|
||||
website/static/img/nous-logo.png 150x150 on white (opaque)
|
||||
website/static/img/nous-logo-dark.png 150x150 on #0d1117 (opaque)
|
||||
website/static/img/favicon-16x16.png 16x16
|
||||
website/static/img/favicon-32x32.png 32x32
|
||||
website/static/img/apple-touch-icon.png 180x180
|
||||
website/static/img/favicon.ico 16,32,48
|
||||
website/static/img/favicon.svg copy of the master
|
||||
website/static/img/favicon.svg copy of the light master
|
||||
web/public/favicon.ico 16,32,48
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import shutil
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -69,10 +97,34 @@ except ImportError:
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
MASTER = ROOT / "assets" / "icon-master.svg"
|
||||
ASSETS = ROOT / "assets"
|
||||
BACKGROUNDS = ASSETS / "backgrounds"
|
||||
GIRL_BLACK = ASSETS / "nous-girl-black.svg"
|
||||
GIRL_WHITE = ASSETS / "nous-girl-white.svg"
|
||||
GIRLS = {"black": GIRL_BLACK, "white": GIRL_WHITE}
|
||||
MASTER = ASSETS / "icon-master.svg"
|
||||
MASTER_DARK = ASSETS / "icon-master-dark.svg"
|
||||
|
||||
# The squircle corner radius measured on the real 1024 icon (~245px).
|
||||
SQUIRCLE_RADIUS = 245
|
||||
# The nous dark background (#0d1117) — fixed dark tile/background everywhere.
|
||||
DARK_HEX = "#0d1117"
|
||||
DARK_RGB = (13, 17, 23)
|
||||
|
||||
# Girl placement per background: (x, y, w, h) in that background's coordinate
|
||||
# space. Squircle backgrounds put the girl in the 824px HIG content safe zone
|
||||
# (centered, 100px pad on a 1024 canvas); logo frames fill the white/dark
|
||||
# interior at ~1.7x; BrandMark tiles take the girl height-fitted (meet).
|
||||
GIRL_BOXES = {
|
||||
"squircle-light.svg": (100, 100, 824, 824),
|
||||
"squircle-dark.svg": (100, 100, 824, 824),
|
||||
"logo-frame.svg": (14, 17, 1743, 1766),
|
||||
"logo-frame-dark.svg": (14, 17, 1743, 1766),
|
||||
"tile-light.svg": (0, 0, 256, 256),
|
||||
"tile-dark.svg": (0, 0, 256, 256),
|
||||
}
|
||||
# The brand-kit SVG canvas (both girl svgs share this viewBox).
|
||||
GIRL_VIEWBOX = 5487.0615
|
||||
|
||||
PLACEHOLDER = f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="{SQUIRCLE_RADIUS}" fill="#ffffff"/>
|
||||
@@ -82,23 +134,35 @@ PLACEHOLDER = f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024
|
||||
|
||||
# (relpath, kind, arg)
|
||||
TARGETS: list[tuple[str, str, object]] = [
|
||||
("assets/icon-master.svg", "svg", None),
|
||||
("assets/icon-master-dark.svg", "svg_dark", None),
|
||||
("apps/desktop/assets/icon.png", "png", 1024),
|
||||
("apps/desktop/assets/icon.ico", "ico", [16, 24, 32, 48, 64, 128, 256]),
|
||||
("apps/desktop/assets/icon.icns", "icns", None),
|
||||
("apps/desktop/assets/icon-dark.png", "png_dark", 1024),
|
||||
("apps/desktop/assets/icon-dark.ico", "ico_dark", [16, 24, 32, 48, 64, 128, 256]),
|
||||
("apps/desktop/assets/icon-dark.icns", "icns_dark", None),
|
||||
("apps/desktop/assets/appx/Wide310x150Logo.png", "wide", (310, 150)),
|
||||
("apps/desktop/assets/appx/StoreLogo.png", "png", 50),
|
||||
("apps/desktop/assets/appx/Square44x44Logo.png", "png", 44),
|
||||
("apps/desktop/assets/appx/Square150x150Logo.png", "png", 150),
|
||||
("apps/desktop/assets/appx/Wide310x150Logo-dark.png", "wide_dark", (310, 150)),
|
||||
("apps/desktop/assets/appx/StoreLogo-dark.png", "png_dark", 50),
|
||||
("apps/desktop/assets/appx/Square44x44Logo-dark.png", "png_dark", 44),
|
||||
("apps/desktop/assets/appx/Square150x150Logo-dark.png", "png_dark", 150),
|
||||
("apps/desktop/public/apple-touch-icon.png", "png", 1024),
|
||||
("apps/desktop/public/nous-girl.jpg", "jpg_white", 256),
|
||||
("apps/desktop/public/nous-girl.png", "girl_light", 256),
|
||||
("apps/desktop/public/nous-girl-dark.png", "girl_dark", 256),
|
||||
("apps/bootstrap-installer/src-tauri/icons/32x32.png", "png", 32),
|
||||
("apps/bootstrap-installer/src-tauri/icons/128x128.png", "png", 128),
|
||||
("apps/bootstrap-installer/src-tauri/icons/128x128@2x.png", "png", 256),
|
||||
("apps/bootstrap-installer/src-tauri/icons/icon.ico", "ico", [16, 32, 64, 128, 256]),
|
||||
("apps/bootstrap-installer/src-tauri/icons/icon.icns", "icns", None),
|
||||
("apps/bootstrap-installer/public/nous-girl.jpg", "jpg_white", 256),
|
||||
("apps/bootstrap-installer/public/nous-girl.png", "girl_light", 256),
|
||||
("website/static/img/logo.png", "logo", None),
|
||||
("website/static/img/logo-dark.png", "logo_dark", None),
|
||||
("website/static/img/nous-logo.png", "png_white", 150),
|
||||
("website/static/img/nous-logo-dark.png", "png_dark_white", 150),
|
||||
("website/static/img/favicon-16x16.png", "png", 16),
|
||||
("website/static/img/favicon-32x32.png", "png", 32),
|
||||
("website/static/img/apple-touch-icon.png", "png", 180),
|
||||
@@ -107,23 +171,92 @@ TARGETS: list[tuple[str, str, object]] = [
|
||||
("web/public/favicon.ico", "ico", [16, 32, 48]),
|
||||
]
|
||||
|
||||
# ─── girl art extraction ────────────────────────────────────────────────────
|
||||
|
||||
def ensure_master(check: bool = False) -> Path:
|
||||
if not MASTER.exists():
|
||||
_path_cache: dict[str, str] = {}
|
||||
_bbox_cache: dict[str, tuple[float, float, float, float]] = {}
|
||||
|
||||
|
||||
def girl_path(girl: str) -> str:
|
||||
"""The girl `<path>` element with editor metadata stripped (resvg rejects
|
||||
undeclared inkscape/sodipodi prefixes)."""
|
||||
if girl not in _path_cache:
|
||||
src = GIRLS[girl].read_text(encoding="utf-8")
|
||||
m = re.search(r"<path\b.*?/>", src, re.S)
|
||||
assert m, f"no <path> found in {GIRLS[girl].name}"
|
||||
path = re.sub(r'\s+(inkscape|sodipodi):[a-zA-Z-]+="[^"]*"', "", m.group(0))
|
||||
_path_cache[girl] = path
|
||||
return _path_cache[girl]
|
||||
|
||||
|
||||
def girl_bbox(girl: str) -> tuple[float, float, float, float]:
|
||||
"""Art bounding box in the girl SVG's coordinate space, measured by
|
||||
rendering once and taking the alpha bbox (robust to art changes)."""
|
||||
if girl not in _bbox_cache:
|
||||
data = resvg_py.svg_to_bytes(svg_path=str(GIRLS[girl]), width=512, height=512)
|
||||
im = Image.open(io.BytesIO(data))
|
||||
bx, by, bx2, by2 = im.getchannel("A").point(lambda v: 255 if v > 0 else 0).getbbox()
|
||||
s = GIRL_VIEWBOX / 512.0
|
||||
_bbox_cache[girl] = (bx * s, by * s, (bx2 - bx) * s, (by2 - by) * s)
|
||||
return _bbox_cache[girl]
|
||||
|
||||
|
||||
def girl_layer(girl: str, box: tuple[float, float, float, float]) -> str:
|
||||
"""Nested-svg layer: girl art (bbox as viewBox) placed into `box` — the
|
||||
box's aspect is preserved via 'meet', so the girl never distorts."""
|
||||
bx, by, bw, bh = girl_bbox(girl)
|
||||
x, y, w, h = box
|
||||
return (
|
||||
f'<svg x="{x}" y="{y}" width="{w}" height="{h}" viewBox="{bx} {by} {bw} {bh}">\n'
|
||||
f" {girl_path(girl)}\n"
|
||||
" </svg>"
|
||||
)
|
||||
|
||||
|
||||
def background_inner(name: str) -> tuple[str, int, int]:
|
||||
"""Inner content + (width, height) of a background SVG asset."""
|
||||
text = (BACKGROUNDS / name).read_text(encoding="utf-8")
|
||||
m = re.search(r"<svg\b[^>]*viewBox=\"0 0 (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)\"[^>]*>", text)
|
||||
assert m, f"cannot parse viewBox of {name}"
|
||||
w, h = float(m.group(1)), float(m.group(2))
|
||||
inner = re.sub(r"^.*?>\s*", "", text, count=1, flags=re.S)
|
||||
inner = re.sub(r"\s*</svg>\s*$", "", inner, flags=re.S)
|
||||
return inner, int(w), int(h)
|
||||
|
||||
|
||||
def compose_master(girl: str, bg: str) -> str:
|
||||
"""Composed master SVG: background + girl layer."""
|
||||
inner, w, h = background_inner(bg)
|
||||
x, y, bx, bh = GIRL_BOXES[bg]
|
||||
return (
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}">\n'
|
||||
f" {inner.strip()}\n"
|
||||
f" {girl_layer(girl, (x, y, bx, bh))}\n"
|
||||
"</svg>\n"
|
||||
)
|
||||
|
||||
|
||||
def ensure_masters(check: bool = False) -> None:
|
||||
"""Write the generated light/dark masters; placeholder fallback if the
|
||||
girl art is missing."""
|
||||
missing = [p for p in (GIRL_BLACK, GIRL_WHITE) if not p.exists()]
|
||||
if missing:
|
||||
if check:
|
||||
sys.exit(
|
||||
f"[check] {MASTER.relative_to(ROOT)} is missing — "
|
||||
"cannot verify generated icons without a master."
|
||||
)
|
||||
MASTER.write_text(PLACEHOLDER, encoding="utf-8")
|
||||
print(f"[master] wrote placeholder: {MASTER.relative_to(ROOT)}")
|
||||
return MASTER
|
||||
sys.exit(f"[check] girl art missing: {[p.name for p in missing]}")
|
||||
for path, content in ((MASTER, PLACEHOLDER), (MASTER_DARK, PLACEHOLDER)):
|
||||
path.write_text(content, encoding="utf-8")
|
||||
print(f"[master] wrote placeholder masters ({[p.name for p in missing]})")
|
||||
return
|
||||
MASTER.write_text(compose_master("black", "squircle-light.svg"), encoding="utf-8")
|
||||
MASTER_DARK.write_text(compose_master("white", "squircle-dark.svg"), encoding="utf-8")
|
||||
|
||||
|
||||
def render(size: int, *, background: str | None = None) -> Image.Image:
|
||||
"""Render the master to an RGBA PNG of `size`x`size`."""
|
||||
# ─── rendering ──────────────────────────────────────────────────────────────
|
||||
|
||||
def render(master: Path, size: int, *, background: str | None = None) -> Image.Image:
|
||||
"""Render a master to an RGBA PNG of `size`x`size`."""
|
||||
data = resvg_py.svg_to_bytes(
|
||||
svg_path=str(MASTER), width=size, height=size, background=background
|
||||
svg_path=str(master), width=size, height=size, background=background
|
||||
)
|
||||
return Image.open(io.BytesIO(data)).convert("RGBA")
|
||||
|
||||
@@ -134,11 +267,37 @@ def paste_centered(canvas: Image.Image, img: Image.Image) -> None:
|
||||
canvas.alpha_composite(img, (x, y))
|
||||
|
||||
|
||||
def build_logo_image() -> Image.Image:
|
||||
"""1772x1799: black frame (~14-17px) + white interior + art at ~1.7x.
|
||||
def girl_mark(kind: str, size: int) -> Image.Image:
|
||||
"""Composite the girl SVG onto a fixed tile (BrandMark asset), rendered
|
||||
straight from the vector art (crisp at any size).
|
||||
|
||||
Measured from the real logo.png: border L/R 14, T 17, B 16; white
|
||||
interior spans (15,18)..(1758,1784) = 1743x1766.
|
||||
girl_light: black girl on white tile. girl_dark: white girl on #0d1117.
|
||||
"""
|
||||
if kind == "girl_light":
|
||||
girl, bg = "black", "tile-light.svg"
|
||||
else:
|
||||
girl, bg = "white", "tile-dark.svg"
|
||||
if not GIRLS[girl].exists():
|
||||
print(f" [girl] {GIRLS[girl].name} missing — placeholder fallback")
|
||||
return render(MASTER if kind == "girl_light" else MASTER_DARK, size)
|
||||
|
||||
inner, w, h = background_inner(bg)
|
||||
svg = (
|
||||
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}">\n'
|
||||
f" {inner.strip()}\n"
|
||||
f" {girl_layer(girl, GIRL_BOXES[bg])}\n"
|
||||
"</svg>\n"
|
||||
)
|
||||
data = resvg_py.svg_to_bytes(svg_string=svg, width=size, height=size)
|
||||
return Image.open(io.BytesIO(data)).convert("RGBA")
|
||||
|
||||
|
||||
def build_logo_image(master: Path, dark: bool = False) -> Image.Image:
|
||||
"""1772x1799 wordmark: frame (light: black / dark: white, ~14-17px) +
|
||||
interior (light: white / dark: #0d1117) + art at ~1.7x.
|
||||
|
||||
Measured from the real logo.png: border L/R 14, T 17, B 16; interior
|
||||
spans (15,18)..(1758,1784) = 1743x1766.
|
||||
"""
|
||||
from PIL import ImageDraw
|
||||
|
||||
@@ -146,55 +305,88 @@ def build_logo_image() -> Image.Image:
|
||||
art_w, art_h = 1743, 1766
|
||||
border = 14
|
||||
top, bottom = 17, 16
|
||||
canvas = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||||
if dark:
|
||||
frame, interior = DARK_RGB, (255, 255, 255)
|
||||
else:
|
||||
frame, interior = (0, 0, 0), (255, 255, 255)
|
||||
|
||||
canvas = Image.new("RGBA", (W, H), (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(canvas)
|
||||
draw.rounded_rectangle((0, 0, W - 1, H - 1), radius=2, fill=(0, 0, 0, 255))
|
||||
draw.rounded_rectangle((0, 0, W - 1, H - 1), radius=2, fill=frame)
|
||||
draw.rectangle(
|
||||
(border, top, W - 1 - border, H - 1 - bottom),
|
||||
fill=(255, 255, 255, 255),
|
||||
fill=interior,
|
||||
)
|
||||
|
||||
# Art: render the master square at ~1.7x, tiny vertical stretch to fill.
|
||||
art = render(art_w).resize((art_w, art_h), Image.LANCZOS)
|
||||
art = render(master, art_w).resize((art_w, art_h), Image.LANCZOS)
|
||||
canvas.alpha_composite(art, (border, top))
|
||||
return canvas
|
||||
|
||||
|
||||
def target_bytes(kind: str, arg: object) -> bytes:
|
||||
"""Produce the exact bytes for one target. Shared by write + check."""
|
||||
if kind == "svg":
|
||||
return MASTER.read_bytes()
|
||||
if kind == "svg_dark":
|
||||
return MASTER_DARK.read_bytes()
|
||||
if kind == "svg_copy":
|
||||
return MASTER.read_bytes()
|
||||
|
||||
buf = io.BytesIO()
|
||||
if kind == "png":
|
||||
render(arg).save(buf, "PNG")
|
||||
render(MASTER, arg).save(buf, "PNG")
|
||||
elif kind == "png_dark":
|
||||
render(MASTER_DARK, arg).save(buf, "PNG")
|
||||
elif kind == "png_white":
|
||||
render(arg, background="#ffffff").save(buf, "PNG")
|
||||
elif kind == "jpg_white":
|
||||
# JPEG has no alpha: flatten the squircle onto white.
|
||||
render(arg, background="#ffffff").convert("RGB").save(buf, "JPEG", quality=90)
|
||||
render(MASTER, arg, background="#ffffff").save(buf, "PNG")
|
||||
elif kind == "png_dark_white":
|
||||
render(MASTER_DARK, arg, background=DARK_HEX).save(buf, "PNG")
|
||||
elif kind == "girl_light" or kind == "girl_dark":
|
||||
# Flat 2-tone vector-style art: <256 distinct colors, so an 8-bit
|
||||
# palette PNG holds it pixel-identically. Saved with compress_level=0
|
||||
# (stored deflate blocks): the icons-freshness lane byte-compares
|
||||
# against an ubuntu regen, and compressed PNG bytes drift per host
|
||||
# zlib — stored blocks are spec-locked, identical on every platform.
|
||||
# ~66KB vs ~15KB compressed is a non-issue for a bundled asset.
|
||||
girl_mark(kind, arg).convert("RGB").convert("P", palette=Image.ADAPTIVE, colors=256).save(
|
||||
buf, "PNG", compress_level=0
|
||||
)
|
||||
elif kind == "ico":
|
||||
img = render(max(arg))
|
||||
img = render(MASTER, max(arg))
|
||||
img.save(buf, format="ICO", sizes=[(s, s) for s in arg])
|
||||
elif kind == "ico_dark":
|
||||
img = render(MASTER_DARK, max(arg))
|
||||
img.save(buf, format="ICO", sizes=[(s, s) for s in arg])
|
||||
elif kind == "icns":
|
||||
img = render(1024)
|
||||
img = render(MASTER, 1024)
|
||||
frames = [img.resize((s, s), Image.LANCZOS) for s in (16, 32, 64, 128, 256, 512, 1024)]
|
||||
img.save(buf, format="ICNS", append_images=frames[1:])
|
||||
elif kind == "icns_dark":
|
||||
img = render(MASTER_DARK, 1024)
|
||||
frames = [img.resize((s, s), Image.LANCZOS) for s in (16, 32, 64, 128, 256, 512, 1024)]
|
||||
img.save(buf, format="ICNS", append_images=frames[1:])
|
||||
elif kind == "wide":
|
||||
w, h = arg
|
||||
canvas = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
paste_centered(canvas, render(100))
|
||||
paste_centered(canvas, render(MASTER, 100))
|
||||
canvas.save(buf, "PNG")
|
||||
elif kind == "wide_dark":
|
||||
w, h = arg
|
||||
canvas = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
paste_centered(canvas, render(MASTER_DARK, 100))
|
||||
canvas.save(buf, "PNG")
|
||||
elif kind == "logo":
|
||||
build_logo_image().save(buf, "PNG")
|
||||
build_logo_image(MASTER, dark=False).save(buf, "PNG")
|
||||
elif kind == "logo_dark":
|
||||
build_logo_image(MASTER_DARK, dark=True).save(buf, "PNG")
|
||||
else:
|
||||
raise ValueError(f"unknown kind {kind!r}")
|
||||
return buf.getvalue()
|
||||
|
||||
|
||||
def cmd_write() -> None:
|
||||
ensure_master()
|
||||
ensure_masters()
|
||||
written = 0
|
||||
for rel, kind, arg in TARGETS:
|
||||
path = ROOT / rel
|
||||
@@ -204,17 +396,17 @@ def cmd_write() -> None:
|
||||
written += 1
|
||||
except Exception as exc: # noqa: BLE001 - report and keep going
|
||||
print(f" !! {rel}: FAILED ({exc})")
|
||||
print(f"[ok] wrote {written}/{len(TARGETS)} files from {MASTER.name}")
|
||||
print(f"[ok] wrote {written}/{len(TARGETS)} files")
|
||||
|
||||
print("\n[verify]")
|
||||
for rel, kind, arg in TARGETS:
|
||||
path = ROOT / rel
|
||||
try:
|
||||
if kind == "svg_copy":
|
||||
if kind in ("svg", "svg_dark", "svg_copy"):
|
||||
print(f" {rel}: {path.stat().st_size} bytes SVG")
|
||||
continue
|
||||
im = Image.open(path)
|
||||
if kind == "ico":
|
||||
if kind in ("ico", "ico_dark"):
|
||||
sizes = []
|
||||
try:
|
||||
for i in range(im.n_frames):
|
||||
@@ -223,7 +415,7 @@ def cmd_write() -> None:
|
||||
except Exception:
|
||||
sizes = [im.size]
|
||||
print(f" {rel}: ICO {sorted(set(sizes))}")
|
||||
elif kind == "icns":
|
||||
elif kind in ("icns", "icns_dark"):
|
||||
print(f" {rel}: ICNS {im.size} (container)")
|
||||
else:
|
||||
print(f" {rel}: {im.format} {im.size}")
|
||||
@@ -232,7 +424,7 @@ def cmd_write() -> None:
|
||||
|
||||
|
||||
def cmd_check() -> int:
|
||||
ensure_master(check=True)
|
||||
ensure_masters(check=True)
|
||||
drifted: list[str] = []
|
||||
for rel, kind, arg in TARGETS:
|
||||
path = ROOT / rel
|
||||
@@ -249,10 +441,10 @@ def cmd_check() -> int:
|
||||
drifted.append(f"{rel}: drift ({len(actual)} bytes on disk vs {len(expected)} generated)")
|
||||
|
||||
if not drifted:
|
||||
print(f"[ok] all {len(TARGETS)} generated icons match {MASTER.name}")
|
||||
print(f"[ok] all {len(TARGETS)} generated icons match the girl art + backgrounds")
|
||||
return 0
|
||||
|
||||
print(f"[check] {len(drifted)} file(s) out of sync with {MASTER.name}:")
|
||||
print(f"[check] {len(drifted)} file(s) out of sync:")
|
||||
for line in drifted:
|
||||
print(f" - {line}")
|
||||
print(
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -121,6 +121,7 @@ const config: Config = {
|
||||
logo: {
|
||||
alt: 'Hermes Agent',
|
||||
src: 'img/logo.png',
|
||||
srcDark: 'img/logo-dark.png',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 458 B |
|
Before Width: | Height: | Size: 607 B After Width: | Height: | Size: 918 B |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -1,4 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<!-- app-icon background, light: white rounded square (rx=245), transparent corners -->
|
||||
<rect x="0" y="0" width="1024" height="1024" rx="245" fill="#ffffff"/>
|
||||
<circle cx="512" cy="512" r="420" fill="#e34c4c"/>
|
||||
<svg x="100" y="100" width="824" height="824" viewBox="182.1875888671875 150.036837890625 4951.215650390624 5337.024662109375">
|
||||
<path
|
||||
id="path14"
|
||||
style="opacity:1;fill:#000000"
|
||||
d="M 2780.0999 2572.1925 C 2731.3999 2572.7925 2683.4019 2574.5933 2673.3019 2576.2933 C 2641.3019 2580.3933 2609.7995 2583.9939 2590.1995 2585.7939 C 2558.6995 2588.6939 2461.4006 2604.1934 2436.0006 2609.4934 C 2430.6006 2610.6934 2417.6007 2613.5942 2407.5007 2615.3942 C 2396.9007 2617.7942 2381.4 2620.6937 2373.1 2622.4937 C 2364.3 2624.2937 2350.0005 2627.1938 2341.1005 2628.9938 C 2297.8005 2637.7938 2215.4009 2657.4932 2195.8009 2662.7932 C 2189.3009 2664.5932 2181.499 2666.2947 2179.199 2666.2947 C 2176.899 2666.2947 2169.8014 2668.0943 2163.8014 2669.7943 C 2158.5014 2671.5943 2146.5999 2674.4938 2138.1999 2676.8938 C 2110.2999 2683.9938 2106.2018 2685.2929 2084.8018 2691.1929 C 2072.9018 2694.6929 2059.8019 2698.2929 2056.3019 2699.4929 C 2034.9019 2705.3929 2015.3015 2711.9934 1989.8015 2720.8934 C 1973.8015 2726.1934 1957.1997 2731.5932 1952.9997 2732.7932 C 1948.2997 2734.5932 1942.3011 2736.2934 1939.9011 2737.4934 C 1937.0011 2738.6934 1925.1005 2742.8921 1913.8005 2746.9921 C 1901.9005 2751.0921 1888.8006 2755.7914 1885.3006 2757.6914 C 1881.1006 2759.4914 1869.9003 2763.5918 1860.3003 2767.0918 C 1850.3003 2770.5918 1839.0002 2774.8924 1835.4002 2776.5924 C 1827.7002 2780.0924 1796.302 2793.7927 1777.302 2801.5927 C 1661.602 2848.9927 1452.2004 2956.3923 1391.6004 2998.4923 C 1387.4004 3001.3923 1370.9005 3011.4918 1354.8005 3022.1918 C 1312.0005 3048.8918 1272.9013 3074.3922 1253.9013 3088.6922 C 1249.2013 3092.1922 1237.2994 3100.5915 1227.1994 3107.6915 C 1207.5994 3121.3915 1120.4013 3186.4936 1103.8013 3200.7936 C 1042.1013 3251.1936 1002.2989 3287.3931 950.69897 3339.5931 C 911.59897 3379.3931 864.10036 3432.7915 851.00036 3451.1915 C 849.20036 3453.5915 841.50097 3464.2936 833.80097 3474.2936 C 813.60097 3501.5936 773.80044 3562.0926 752.50044 3599.9926 C 713.90044 3666.9926 647.50056 3803.4919 635.60056 3839.0919 C 632.70056 3847.8919 629.00156 3855.6932 627.80156 3856.2932 C 626.60156 3857.4932 625.99975 3859.7927 625.99975 3861.5927 C 625.99975 3863.3927 621.90105 3876.992 616.50105 3890.692 C 609.40105 3910.892 594.60095 3954.7915 583.30095 3992.6915 C 580.90095 3998.5915 572.69995 4030.0928 568.99995 4044.8928 C 561.79995 4073.3928 552.3999 4117.2925 552.3999 4122.0925 C 552.3999 4126.8925 551.20065 4130.4923 550.00065 4133.9923 C 548.80065 4136.8923 546.50045 4147.6925 545.30045 4157.0925 C 544.10045 4166.4925 540.59977 4190.2931 537.59977 4209.8931 C 532.89977 4242.4931 532.30212 4253.6937 532.30212 4342.7937 C 532.30212 4431.8937 532.89873 4451.2934 541.69873 4502.8934 C 549.49873 4546.1934 559.50006 4582.3942 575.50006 4623.8942 C 578.40006 4630.9942 580.80149 4638.1924 580.80149 4638.6924 C 580.80149 4639.8924 589.70013 4658.2925 600.40013 4680.1925 C 617.00013 4713.9925 625.99914 4728.8923 643.19914 4755.5923 C 655.09914 4773.9923 687.60031 4810.7922 715.60031 4837.4922 C 733.40031 4855.2922 750.00089 4870.6928 751.80089 4871.8928 C 753.60089 4873.0928 762.50077 4880.2942 772.00077 4887.2942 C 815.80077 4922.2942 897.69957 4966.7939 938.09957 4977.3939 C 941.59957 4978.5939 952.89964 4982.0941 962.99964 4985.7941 C 1000.3996 4998.2941 1026.501 5005.3935 1031.801 5002.9935 C 1034.201 5002.3935 1033.6004 5001.1932 1030.6004 5000.0932 C 1017.5004 4994.6932 987.80012 4972.1933 970.60012 4954.9933 C 960.60012 4943.6933 945.70125 4925.8918 938.00125 4914.6918 C 923.20125 4892.7918 892.90185 4838.7923 885.80185 4820.9923 C 883.40185 4815.0923 879.3008 4805.5921 877.0008 4799.6921 C 874.6008 4793.7921 871.70069 4786.5945 870.50069 4784.2945 C 867.60069 4778.3945 857.9995 4751.0939 851.4995 4731.4939 C 846.1995 4716.6939 845.59958 4707.0929 849.69958 4707.0929 C 851.49958 4706.4929 857.39972 4717.0931 864.49972 4730.1931 C 892.39972 4784.7931 936.9016 4841.7937 987.9016 4887.9937 C 1020.5016 4917.6937 1072.1013 4958.5931 1078.0013 4958.5931 C 1083.9013 4958.5931 1082.0991 4960.9939 1085.1991 4964.4939 L 1091.6992 4969.7934 L 1089.9011 4999.4939 C 1088.7011 5028.5939 1085.1995 5067.6918 1076.1995 5153.1918 C 1073.7995 5178.7918 1070.3 5212.5939 1069.1 5227.8939 C 1067.9 5243.2939 1066.2009 5266.9933 1064.9009 5280.0933 C 1063.7009 5293.0933 1060.8001 5323.3922 1059.0001 5347.6922 C 1056.6001 5371.9922 1053.7013 5403.9928 1051.3013 5418.8928 C 1049.5013 5433.6928 1048.401 5450.8931 1048.401 5456.7931 C 1048.401 5462.6931 1046.6021 5481.7921 1044.3021 5499.5921 C 1038.4021 5553.5921 1034.1997 5592.0928 1031.1997 5620.5928 C 1027.6997 5658.4928 1022.3018 5708.9931 1019.3018 5728.4931 C 1016.9018 5748.6931 1010.5013 5801.9917 1008.0013 5828.1917 C 1004.5013 5861.9917 998.50199 5911.893 997.30199 5915.393 C 996.10199 5917.193 994.90048 5927.8919 993.80048 5939.6919 C 989.70048 5974.6919 986.70083 5997.1924 982.00083 6031.0924 C 979.10083 6048.8924 976.10138 6070.7934 974.90138 6080.3934 C 973.70138 6090.4934 972.00176 6099.993 971.40176 6102.293 C 970.80176 6104.093 969.00025 6113.5928 967.90025 6122.4928 C 965.50025 6138.4928 963.20171 6150.994 954.80171 6194.894 C 952.40171 6206.Line truncated
|
||||
transform="matrix(1.0330354,0,0,1.0330354,-145.41428,-2499.328)" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 4.9 KiB |