-
Notifications
You must be signed in to change notification settings - Fork 56
feat: favicon from logo-icon.svg — all required sizes, ICO + PNG (Closes #471) #673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| { | ||
| "name": "SolFoundry", | ||
| "short_name": "SolFoundry", | ||
| "description": "Autonomous AI Software Factory on Solana", | ||
| "start_url": "/", | ||
| "display": "standalone", | ||
| "background_color": "#0f0f23", | ||
| "theme_color": "#9945FF", | ||
| "icons": [ | ||
| { | ||
| "src": "/assets/favicons/favicon-192x192.png", | ||
| "sizes": "192x192", | ||
| "type": "image/png", | ||
| "purpose": "any" | ||
| }, | ||
| { | ||
| "src": "/assets/favicons/favicon-192x192.png", | ||
| "sizes": "192x192", | ||
| "type": "image/png", | ||
| "purpose": "maskable" | ||
| }, | ||
| { | ||
| "src": "/assets/favicons/favicon-512x512.png", | ||
| "sizes": "512x512", | ||
| "type": "image/png", | ||
| "purpose": "any" | ||
| }, | ||
| { | ||
| "src": "/assets/favicons/favicon-512x512.png", | ||
| "sizes": "512x512", | ||
| "type": "image/png", | ||
| "purpose": "maskable" | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,15 @@ | |
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>SolFoundry — Autonomous AI Software Factory on Solana</title> | ||
| <!-- Favicons --> | ||
| <link rel="icon" type="image/x-icon" href="/assets/favicons/favicon.ico"> | ||
| <link rel="icon" type="image/png" sizes="32x32" href="/assets/favicons/favicon.ico"> | ||
| <link rel="icon" type="image/png" sizes="16x16" href="/assets/favicons/favicon.ico"> | ||
|
Comment on lines
+9
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MIME type mismatch: Lines 9-10 declare The generation script ( Options:
🤖 Prompt for AI Agents |
||
| <link rel="apple-touch-icon" sizes="180x180" href="/assets/favicons/apple-touch-icon.png"> | ||
| <link rel="icon" type="image/png" sizes="192x192" href="/assets/favicons/favicon-192x192.png"> | ||
| <link rel="icon" type="image/png" sizes="512x512" href="/assets/favicons/favicon-512x512.png"> | ||
| <link rel="manifest" href="/assets/favicons/site.webmanifest"> | ||
| <meta name="theme-color" content="#9945FF"> | ||
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | ||
| <style> | ||
| * { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| #!/usr/bin/env bash | ||
| # generate-favicons.sh — Regenerate favicon assets from assets/logo-icon.svg | ||
| # Usage: ./scripts/generate-favicons.sh | ||
| # Requires: rsvg-convert (librsvg) and ImageMagick 6 (convert) or 7 (magick) | ||
| set -euo pipefail | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" | ||
| SOURCE_SVG="$REPO_ROOT/assets/logo-icon.svg" | ||
| OUT_DIR="$REPO_ROOT/assets/favicons" | ||
|
|
||
| # ── Dependency checks ──────────────────────────────────────────────────────── | ||
|
|
||
| if ! command -v rsvg-convert &>/dev/null; then | ||
| echo "ERROR: rsvg-convert not found. Install with: apt install librsvg2-bin / brew install librsvg" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| if command -v magick &>/dev/null; then | ||
| IMAGEMAGICK_CMD="magick" | ||
| elif command -v convert &>/dev/null; then | ||
| IMAGEMAGICK_CMD="convert" | ||
| else | ||
| echo "ERROR: ImageMagick not found (need 'magick' or 'convert'). Install with: apt install imagemagick / brew install imagemagick" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| if [[ ! -f "$SOURCE_SVG" ]]; then | ||
| echo "ERROR: Source SVG not found: $SOURCE_SVG" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| mkdir -p "$OUT_DIR" | ||
|
|
||
| echo "Generating favicons from $SOURCE_SVG using $IMAGEMAGICK_CMD …" | ||
|
|
||
| # ── PNG sizes ──────────────────────────────────────────────────────────────── | ||
|
|
||
| rsvg-convert -w 16 -h 16 "$SOURCE_SVG" -o /tmp/_favicon-16.png | ||
| rsvg-convert -w 32 -h 32 "$SOURCE_SVG" -o /tmp/_favicon-32.png | ||
|
Comment on lines
+39
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Insecure use of predictable temp file paths in shared Using hardcoded paths like Use 🔒 Proposed fix using mktemp+# Create secure temp directory
+TMPDIR=$(mktemp -d)
+trap 'rm -rf "$TMPDIR"' EXIT
+
# ── PNG sizes ────────────────────────────────────────────────────────────────
-rsvg-convert -w 16 -h 16 "$SOURCE_SVG" -o /tmp/_favicon-16.png
-rsvg-convert -w 32 -h 32 "$SOURCE_SVG" -o /tmp/_favicon-32.png
+rsvg-convert -w 16 -h 16 "$SOURCE_SVG" -o "$TMPDIR/favicon-16.png"
+rsvg-convert -w 32 -h 32 "$SOURCE_SVG" -o "$TMPDIR/favicon-32.png"
rsvg-convert -w 180 -h 180 "$SOURCE_SVG" -o "$OUT_DIR/apple-touch-icon.png"
rsvg-convert -w 192 -h 192 "$SOURCE_SVG" -o "$OUT_DIR/favicon-192x192.png"
rsvg-convert -w 512 -h 512 "$SOURCE_SVG" -o "$OUT_DIR/favicon-512x512.png" # ── Multi-size ICO (embeds 16x16 + 32x32) ────────────────────────────────────
-"$IMAGEMAGICK_CMD" /tmp/_favicon-16.png /tmp/_favicon-32.png "$OUT_DIR/favicon.ico"
-rm -f /tmp/_favicon-16.png /tmp/_favicon-32.png
+"$IMAGEMAGICK_CMD" "$TMPDIR/favicon-16.png" "$TMPDIR/favicon-32.png" "$OUT_DIR/favicon.ico"
+# Cleanup handled by trapAlso applies to: 51-52 🤖 Prompt for AI Agents |
||
| rsvg-convert -w 180 -h 180 "$SOURCE_SVG" -o "$OUT_DIR/apple-touch-icon.png" | ||
| rsvg-convert -w 192 -h 192 "$SOURCE_SVG" -o "$OUT_DIR/favicon-192x192.png" | ||
| rsvg-convert -w 512 -h 512 "$SOURCE_SVG" -o "$OUT_DIR/favicon-512x512.png" | ||
|
|
||
| echo " ✔ apple-touch-icon.png (180x180)" | ||
| echo " ✔ favicon-192x192.png (192x192)" | ||
| echo " ✔ favicon-512x512.png (512x512)" | ||
|
|
||
| # ── Multi-size ICO (embeds 16x16 + 32x32) ──────────────────────────────────── | ||
|
|
||
| "$IMAGEMAGICK_CMD" /tmp/_favicon-16.png /tmp/_favicon-32.png "$OUT_DIR/favicon.ico" | ||
| rm -f /tmp/_favicon-16.png /tmp/_favicon-32.png | ||
|
|
||
| echo " ✔ favicon.ico (16x16 + 32x32 embedded)" | ||
| echo "" | ||
| echo "Done. 4 files in $OUT_DIR" | ||
| echo "Binary count: $(find "$OUT_DIR" -maxdepth 1 \( -name '*.png' -o -name '*.ico' \) | wc -l | tr -d ' ')" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| #!/usr/bin/env bash | ||
| # test-favicons.sh — Verify favicon assets are present and correctly referenced | ||
| set -euo pipefail | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" | ||
| FAVICONS="$REPO_ROOT/assets/favicons" | ||
| INDEX="$REPO_ROOT/index.html" | ||
| MANIFEST="$FAVICONS/site.webmanifest" | ||
| GENERATE="$REPO_ROOT/scripts/generate-favicons.sh" | ||
|
|
||
| PASS=0 | ||
| FAIL=0 | ||
|
|
||
| assert_file() { | ||
| if [[ -f "$1" ]]; then | ||
| echo " ✔ $2" | ||
| ((PASS++)) || true | ||
| else | ||
| echo " ✗ $2 — MISSING: $1" | ||
| ((FAIL++)) || true | ||
| fi | ||
| } | ||
|
|
||
| assert_contains() { | ||
| if grep -q "$2" "$1" 2>/dev/null; then | ||
| echo " ✔ $3" | ||
| ((PASS++)) || true | ||
| else | ||
| echo " ✗ $3 — not found in $1" | ||
| ((FAIL++)) || true | ||
| fi | ||
| } | ||
|
|
||
| assert_min_size() { | ||
| local size | ||
| size=$(wc -c < "$1" 2>/dev/null || echo 0) | ||
| if [[ "$size" -ge "$2" ]]; then | ||
| echo " ✔ $3 (${size} bytes)" | ||
| ((PASS++)) || true | ||
| else | ||
| echo " ✗ $3 — too small (${size} bytes, expected ≥$2)" | ||
| ((FAIL++)) || true | ||
| fi | ||
| } | ||
|
|
||
| echo "=== Favicon Asset Tests ===" | ||
| echo "" | ||
| echo "── Files present ──────────────────────────────────" | ||
| assert_file "$FAVICONS/favicon.ico" "favicon.ico exists" | ||
| assert_file "$FAVICONS/apple-touch-icon.png" "apple-touch-icon.png exists (180x180)" | ||
| assert_file "$FAVICONS/favicon-192x192.png" "favicon-192x192.png exists" | ||
| assert_file "$FAVICONS/favicon-512x512.png" "favicon-512x512.png exists" | ||
| assert_file "$FAVICONS/site.webmanifest" "site.webmanifest exists" | ||
|
|
||
| echo "" | ||
| echo "── File sizes (non-empty) ──────────────────────────" | ||
| [[ -f "$FAVICONS/favicon.ico" ]] && assert_min_size "$FAVICONS/favicon.ico" 1024 "favicon.ico is non-trivial (≥1KB)" | ||
| [[ -f "$FAVICONS/apple-touch-icon.png" ]] && assert_min_size "$FAVICONS/apple-touch-icon.png" 5000 "apple-touch-icon.png is non-trivial (≥5KB)" | ||
| [[ -f "$FAVICONS/favicon-192x192.png" ]] && assert_min_size "$FAVICONS/favicon-192x192.png" 5000 "favicon-192x192.png is non-trivial (≥5KB)" | ||
| [[ -f "$FAVICONS/favicon-512x512.png" ]] && assert_min_size "$FAVICONS/favicon-512x512.png" 20000 "favicon-512x512.png is non-trivial (≥20KB)" | ||
|
Comment on lines
+58
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider unconditional size assertions for clearer test failure reporting. The pattern For more explicit test behavior, consider always calling ♻️ Optional: Handle missing files in assert_min_size assert_min_size() {
local size
+ if [[ ! -f "$1" ]]; then
+ echo " ✗ $3 — file not found: $1"
+ ((FAIL++)) || true
+ return
+ fi
size=$(wc -c < "$1" 2>/dev/null || echo 0)Then remove the 🤖 Prompt for AI Agents |
||
|
|
||
| echo "" | ||
| echo "── Binary file count (must be ≤5) ─────────────────" | ||
| BINARY_COUNT=$(find "$FAVICONS" -maxdepth 1 \( -name '*.png' -o -name '*.ico' \) | wc -l | tr -d ' ') | ||
| if [[ "$BINARY_COUNT" -le 5 ]]; then | ||
| echo " ✔ Binary count = $BINARY_COUNT (≤5)" | ||
| ((PASS++)) || true | ||
| else | ||
| echo " ✗ Binary count = $BINARY_COUNT — exceeds limit of 5!" | ||
| ((FAIL++)) || true | ||
| fi | ||
|
|
||
| echo "" | ||
| echo "── index.html references ──────────────────────────" | ||
| assert_contains "$INDEX" 'favicon.ico' "index.html: favicon.ico link" | ||
| assert_contains "$INDEX" 'apple-touch-icon' "index.html: apple-touch-icon link" | ||
| assert_contains "$INDEX" 'favicon-192x192' "index.html: favicon-192x192 link" | ||
| assert_contains "$INDEX" 'favicon-512x512' "index.html: favicon-512x512 link" | ||
| assert_contains "$INDEX" 'site.webmanifest' "index.html: manifest link" | ||
| assert_contains "$INDEX" 'theme-color' "index.html: theme-color meta" | ||
|
|
||
| echo "" | ||
| echo "── site.webmanifest content ────────────────────────" | ||
| assert_contains "$MANIFEST" '"name"' "manifest: name field" | ||
| assert_contains "$MANIFEST" '"icons"' "manifest: icons array" | ||
| assert_contains "$MANIFEST" '"purpose"' "manifest: icon purpose field" | ||
| assert_contains "$MANIFEST" '"192x192"' "manifest: 192x192 icon entry" | ||
| assert_contains "$MANIFEST" '"512x512"' "manifest: 512x512 icon entry" | ||
| assert_contains "$MANIFEST" '"maskable"' "manifest: maskable purpose present" | ||
|
|
||
| echo "" | ||
| echo "── generate-favicons.sh ────────────────────────────" | ||
| assert_file "$GENERATE" "generate-favicons.sh exists" | ||
| assert_contains "$GENERATE" 'rsvg-convert' "script: uses rsvg-convert" | ||
| assert_contains "$GENERATE" 'IMAGEMAGICK_CMD' "script: ImageMagick 6/7 compat var" | ||
| assert_contains "$GENERATE" 'magick' "script: detects magick (IM7)" | ||
| assert_contains "$GENERATE" 'convert' "script: fallback to convert (IM6)" | ||
| assert_contains "$GENERATE" 'logo-icon.svg' "script: uses logo-icon.svg as source" | ||
|
|
||
| echo "" | ||
| echo "=== Results: ${PASS} passed, ${FAIL} failed ===" | ||
| [[ "$FAIL" -eq 0 ]] && exit 0 || exit 1 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
Repository: SolFoundry/solfoundry
Length of output: 261
🏁 Script executed:
Repository: SolFoundry/solfoundry
Length of output: 3092
Maskable icons may have suboptimal background clipping—verify PNG generation handles this properly.
The same PNG files are used for both
purpose: "any"andpurpose: "maskable"(lines 16-21 and 28-33). The source SVG (assets/logo-icon.svg) shows the background gradient extends to the full 512×512 canvas edges, which will be clipped when Android masks the icon to circular or other shapes.The main anvil/hammer icon content is centered and has adequate padding, but the background treatment may result in visual issues on devices that apply masking. Verify that:
🤖 Prompt for AI Agents