Files
CxIDE/scripts/generate-icon.sh
T
cx-git-agent c118996746 feat: CxIDE v1 — native macOS SwiftUI IDE with agentic AI assistant
- SwiftUI macOS app with C++17 code analysis engine (ObjC++ bridge)
- Agentic AI loop: LLM plans → tool calls → execution → feedback loop
- 15 agent tools: file ops, terminal, git, xcode build, code intel
- 7 persistent terminal tools with background session management
- Chat sidebar with agent step rendering and auto-apply
- NVIDIA NIM API integration (Llama 3.3 70B default)
- OpenAI tool_calls format with prompt-based fallback
- Code editor with syntax highlighting and multi-tab support
- File tree, console view, terminal view
- Git integration and workspace management
2026-04-21 16:05:52 -05:00

288 lines
9.4 KiB
Bash
Executable File

#!/bin/bash
# generate-icon.sh — Generate CxIDE app icon at all required sizes
# Uses macOS built-in sips for resizing
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ICON_DIR="$PROJECT_DIR/Assets.xcassets/AppIcon.appiconset"
TMP_DIR=$(mktemp -d)
# Generate a 1024x1024 base icon using Python + CoreGraphics
python3 << 'PYEOF'
import subprocess, os, tempfile, json
# Create SVG-like icon using HTML canvas rendered via sips
# Instead, use the system Python to generate via CoreGraphics
svg = '''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#1a1a2e"/>
<stop offset="100%" style="stop-color:#16213e"/>
</linearGradient>
<linearGradient id="accent" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6666f2"/>
<stop offset="100%" style="stop-color:#4ecdc4"/>
</linearGradient>
</defs>
<!-- Rounded rect background -->
<rect x="0" y="0" width="1024" height="1024" rx="220" ry="220" fill="url(#bg)"/>
<!-- Code bracket left -->
<text x="200" y="620" font-family="SF Mono, Menlo, monospace" font-size="420" font-weight="bold" fill="url(#accent)" opacity="0.9">&lt;</text>
<!-- Cx text -->
<text x="340" y="610" font-family="SF Pro Display, Helvetica Neue, sans-serif" font-size="340" font-weight="800" fill="white" letter-spacing="-10">Cx</text>
<!-- Code bracket right -->
<text x="680" y="620" font-family="SF Mono, Menlo, monospace" font-size="420" font-weight="bold" fill="url(#accent)" opacity="0.9">/&gt;</text>
<!-- Subtle bottom glow -->
<ellipse cx="512" cy="820" rx="300" ry="40" fill="#4ecdc4" opacity="0.15"/>
</svg>'''
tmp = tempfile.mkdtemp()
svg_path = os.path.join(tmp, 'icon.svg')
with open(svg_path, 'w') as f:
f.write(svg)
print(svg_path)
PYEOF
echo "Generating CxIDE app icon..."
# Check for rsvg-convert or use qlmanage as fallback
SVG_PATH=$(python3 -c "
import tempfile, os
svg = '''<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\" width=\"1024\" height=\"1024\">
<defs>
<linearGradient id=\"bg\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">
<stop offset=\"0%\" style=\"stop-color:#1a1a2e\"/>
<stop offset=\"100%\" style=\"stop-color:#16213e\"/>
</linearGradient>
<linearGradient id=\"accent\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">
<stop offset=\"0%\" style=\"stop-color:#6666f2\"/>
<stop offset=\"100%\" style=\"stop-color:#4ecdc4\"/>
</linearGradient>
</defs>
<rect x=\"0\" y=\"0\" width=\"1024\" height=\"1024\" rx=\"220\" ry=\"220\" fill=\"url(#bg)\"/>
<text x=\"200\" y=\"620\" font-family=\"SF Mono, Menlo, monospace\" font-size=\"420\" font-weight=\"bold\" fill=\"url(#accent)\" opacity=\"0.9\">&lt;</text>
<text x=\"340\" y=\"610\" font-family=\"SF Pro Display, Helvetica Neue\" font-size=\"340\" font-weight=\"800\" fill=\"white\">&gt;_</text>
<text x=\"680\" y=\"620\" font-family=\"SF Mono, Menlo, monospace\" font-size=\"420\" font-weight=\"bold\" fill=\"url(#accent)\" opacity=\"0.9\">/&gt;</text>
<ellipse cx=\"512\" cy=\"820\" rx=\"300\" ry=\"40\" fill=\"#4ecdc4\" opacity=\"0.15\"/>
</svg>'''
tmp = tempfile.mkdtemp()
p = os.path.join(tmp, 'icon.svg')
with open(p, 'w') as f: f.write(svg)
print(p)
")
BASE_PNG="$TMP_DIR/icon_1024.png"
# Try rsvg-convert first, fall back to qlmanage
if command -v rsvg-convert &>/dev/null; then
rsvg-convert -w 1024 -h 1024 "$SVG_PATH" -o "$BASE_PNG"
elif command -v qlmanage &>/dev/null; then
qlmanage -t -s 1024 -o "$TMP_DIR" "$SVG_PATH" 2>/dev/null
mv "$TMP_DIR/icon.svg.png" "$BASE_PNG" 2>/dev/null || true
fi
# If SVG rendering failed, generate a simple icon with Python
if [ ! -f "$BASE_PNG" ]; then
python3 << PYEOF2
import struct, zlib, os
def create_png(width, height, pixels):
"""Create a minimal PNG file from raw RGBA pixel data."""
def chunk(chunk_type, data):
c = chunk_type + data
return struct.pack('>I', len(data)) + c + struct.pack('>I', zlib.crc32(c) & 0xffffffff)
header = b'\x89PNG\r\n\x1a\n'
ihdr = chunk(b'IHDR', struct.pack('>IIBBBBB', width, height, 8, 6, 0, 0, 0))
raw = b''
for y in range(height):
raw += b'\x00' # filter byte
for x in range(width):
idx = (y * width + x) * 4
raw += bytes(pixels[idx:idx+4])
idat = chunk(b'IDAT', zlib.compress(raw, 9))
iend = chunk(b'IEND', b'')
return header + ihdr + idat + iend
W = H = 1024
pixels = [0] * (W * H * 4)
def set_pixel(x, y, r, g, b, a=255):
if 0 <= x < W and 0 <= y < H:
idx = (y * W + x) * 4
# Alpha blend
old_a = pixels[idx+3] / 255.0
new_a = a / 255.0
out_a = new_a + old_a * (1 - new_a)
if out_a > 0:
pixels[idx] = int((r * new_a + pixels[idx] * old_a * (1 - new_a)) / out_a)
pixels[idx+1] = int((g * new_a + pixels[idx+1] * old_a * (1 - new_a)) / out_a)
pixels[idx+2] = int((b * new_a + pixels[idx+2] * old_a * (1 - new_a)) / out_a)
pixels[idx+3] = int(out_a * 255)
def fill_rounded_rect(x0, y0, x1, y1, radius, r, g, b, a=255):
for y in range(y0, y1):
for x in range(x0, x1):
# Check corners
inside = True
for cx, cy in [(x0+radius, y0+radius), (x1-radius, y0+radius),
(x0+radius, y1-radius), (x1-radius, y1-radius)]:
dx = x - cx
dy = y - cy
if ((x < x0+radius or x >= x1-radius) and
(y < y0+radius or y >= y1-radius)):
if dx*dx + dy*dy > radius*radius:
inside = False
break
if inside:
# Gradient: dark blue
t = (x - x0) / (x1 - x0) * 0.3 + (y - y0) / (y1 - y0) * 0.7
rr = int(r * (1-t*0.2))
gg = int(g * (1-t*0.1))
bb = int(b * (1+t*0.1))
set_pixel(x, y, min(rr,255), min(gg,255), min(bb,255), a)
# Background - dark midnight blue with rounded corners
fill_rounded_rect(0, 0, 1024, 1024, 220, 26, 26, 46)
# Draw ">_" cursor symbol in center
def fill_rect(x0, y0, w, h, r, g, b, a=255):
for y in range(y0, y0+h):
for x in range(x0, x0+w):
set_pixel(x, y, r, g, b, a)
# Greater-than sign ">"
# Top bar of >
for i in range(180):
x = 280 + i
y = 340 + i // 2
for t in range(28):
set_pixel(x, y+t, 102, 102, 242)
# Bottom bar of >
for i in range(180):
x = 280 + i
y = 620 - i // 2
for t in range(28):
set_pixel(x, y+t, 78, 205, 196)
# Underscore cursor
fill_rect(490, 620, 240, 30, 255, 255, 255)
# Small dot decoration
for dy in range(-8, 9):
for dx in range(-8, 9):
if dx*dx + dy*dy <= 64:
set_pixel(512 + dx, 780 + dy, 78, 205, 196, 100)
png_data = create_png(W, H, pixels)
with open("$TMP_DIR/icon_1024.png", 'wb') as f:
f.write(png_data)
print("Generated 1024x1024 icon")
PYEOF2
fi
if [ ! -f "$BASE_PNG" ]; then
echo "Error: Could not generate base icon"
exit 1
fi
# Generate all required sizes
SIZES=("16" "32" "64" "128" "256" "512" "1024")
for SIZE in "${SIZES[@]}"; do
cp "$BASE_PNG" "$TMP_DIR/icon_${SIZE}.png"
sips -z "$SIZE" "$SIZE" "$TMP_DIR/icon_${SIZE}.png" --out "$ICON_DIR/icon_${SIZE}x${SIZE}.png" >/dev/null 2>&1
done
# Also create @2x versions
sips -z 32 32 "$BASE_PNG" --out "$ICON_DIR/icon_16x16@2x.png" >/dev/null 2>&1
sips -z 64 64 "$BASE_PNG" --out "$ICON_DIR/icon_32x32@2x.png" >/dev/null 2>&1
sips -z 256 256 "$BASE_PNG" --out "$ICON_DIR/icon_128x128@2x.png" >/dev/null 2>&1
sips -z 512 512 "$BASE_PNG" --out "$ICON_DIR/icon_256x256@2x.png" >/dev/null 2>&1
sips -z 1024 1024 "$BASE_PNG" --out "$ICON_DIR/icon_512x512@2x.png" >/dev/null 2>&1
# Update Contents.json
cat > "$ICON_DIR/Contents.json" << 'ICONJSON'
{
"images" : [
{
"filename" : "icon_16x16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "icon_16x16@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "icon_32x32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "icon_32x32@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "icon_128x128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "icon_128x128@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "icon_256x256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "icon_256x256@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "icon_512x512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "icon_512x512@2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
ICONJSON
# Cleanup
rm -rf "$TMP_DIR"
echo "✓ App icon generated at $ICON_DIR"
ls -la "$ICON_DIR"/*.png 2>/dev/null | wc -l | xargs -I{} echo " {} PNG files created"