SPB Git

spb/zyquo-local Public MIT

Native macOS AI chat that runs LLMs 100% locally on Apple Silicon with MLX — no cloud, no API keys.

Swift 97.2% Shell 1.8% Makefile 1%

phase5: Z-core silicon icon (SVG source), icns pipeline, menu bar template

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
simon-pierre boucher committed 12 days ago (Jul 30, 2026) parent 33e125f

Showing 11 changed files with +386 and −1

modified Makefile +1 −0
@@ -54,6 +54,7 @@ define assemble_bundle
54 54 [ -e "$$b" ] && cp -R "$$b" "$(APP_BUNDLE)/Contents/Resources/" || true; \
55 55 done
56 56 [ -f assets/icon/AppIcon.icns ] && cp assets/icon/AppIcon.icns "$(APP_BUNDLE)/Contents/Resources/AppIcon.icns" || true
57 + [ -d assets/icon/menubar ] && cp assets/icon/menubar/MenuBarIcon*.png "$(APP_BUNDLE)/Contents/Resources/" || true
57 58 endef
58 59
59 60 bundle-debug: build
added assets/icon/AppIcon.icns +0 −0

Binary file not shown.

added assets/icon/menubar/MenuBarIcon.png +0 −0

Binary file not shown.

added assets/icon/menubar/MenuBarIcon@2x.png +0 −0

Binary file not shown.

added assets/icon/zyquo-local-chip.svg +94 −0
@@ -0,0 +1,94 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + zyquo-local-chip.svg (variant A: Z-chip)
4 + Zyquo Local
5 +
6 + Author: Simon-Pierre Boucher
7 + Mail: contact@spboucher.ai
8 +-->
9 +<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
10 + <defs>
11 + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
12 + <stop offset="0" stop-color="#1E2622"/>
13 + <stop offset="1" stop-color="#0F1412"/>
14 + </linearGradient>
15 + <linearGradient id="emerald" x1="0" y1="0" x2="0" y2="1" gradientUnits="userSpaceOnUse">
16 + <stop offset="0" stop-color="#17C787"/>
17 + <stop offset="1" stop-color="#0E9F6E"/>
18 + </linearGradient>
19 + <radialGradient id="coreGlow" cx="0.5" cy="0.5" r="0.5">
20 + <stop offset="0" stop-color="#17C787" stop-opacity="0.28"/>
21 + <stop offset="0.7" stop-color="#17C787" stop-opacity="0.07"/>
22 + <stop offset="1" stop-color="#17C787" stop-opacity="0"/>
23 + </radialGradient>
24 + <filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
25 + <feGaussianBlur in="SourceGraphic" stdDeviation="14" result="blur"/>
26 + <feMerge>
27 + <feMergeNode in="blur"/>
28 + <feMergeNode in="SourceGraphic"/>
29 + </feMerge>
30 + </filter>
31 + <filter id="softGlow" x="-60%" y="-60%" width="220%" height="220%">
32 + <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="blur"/>
33 + <feMerge>
34 + <feMergeNode in="blur"/>
35 + <feMergeNode in="SourceGraphic"/>
36 + </feMerge>
37 + </filter>
38 + <clipPath id="squircle">
39 + <rect x="100" y="100" width="824" height="824" rx="184" ry="184"/>
40 + </clipPath>
41 + </defs>
42 +
43 + <g clip-path="url(#squircle)">
44 + <!-- graphite field -->
45 + <rect x="100" y="100" width="824" height="824" fill="url(#bg)"/>
46 + <!-- faint vignette light from top -->
47 + <ellipse cx="512" cy="240" rx="520" ry="300" fill="#2A342E" opacity="0.35"/>
48 + <!-- center glow behind the die -->
49 + <circle cx="512" cy="512" r="420" fill="url(#coreGlow)"/>
50 +
51 + <!-- traces radiating from the die (pins) -->
52 + <g stroke="#3A4540" stroke-width="10" stroke-linecap="round">
53 + <!-- top pins -->
54 + <line x1="400" y1="296" x2="400" y2="196"/>
55 + <line x1="512" y1="296" x2="512" y2="176"/>
56 + <line x1="624" y1="296" x2="624" y2="196"/>
57 + <!-- bottom pins -->
58 + <line x1="400" y1="728" x2="400" y2="828"/>
59 + <line x1="512" y1="728" x2="512" y2="848"/>
60 + <line x1="624" y1="728" x2="624" y2="828"/>
61 + <!-- left pins -->
62 + <line x1="296" y1="400" x2="196" y2="400"/>
63 + <line x1="296" y1="512" x2="176" y2="512"/>
64 + <line x1="296" y1="624" x2="196" y2="624"/>
65 + <!-- right pins -->
66 + <line x1="728" y1="400" x2="828" y2="400"/>
67 + <line x1="728" y1="512" x2="848" y2="512"/>
68 + <line x1="728" y1="624" x2="828" y2="624"/>
69 + </g>
70 + <!-- trace terminal nodes -->
71 + <g fill="#46534D">
72 + <circle cx="400" cy="196" r="9"/><circle cx="512" cy="176" r="9"/><circle cx="624" cy="196" r="9"/>
73 + <circle cx="400" cy="828" r="9"/><circle cx="512" cy="848" r="9"/><circle cx="624" cy="828" r="9"/>
74 + <circle cx="196" cy="400" r="9"/><circle cx="176" cy="512" r="9"/><circle cx="196" cy="624" r="9"/>
75 + <circle cx="828" cy="400" r="9"/><circle cx="848" cy="512" r="9"/><circle cx="828" cy="624" r="9"/>
76 + </g>
77 +
78 + <!-- silicon die -->
79 + <rect x="296" y="296" width="432" height="432" rx="56" fill="#161C19" stroke="#3E4A44" stroke-width="6"/>
80 + <rect x="316" y="316" width="392" height="392" rx="44" fill="none" stroke="#2A332E" stroke-width="3"/>
81 +
82 + <!-- the Z core, glowing -->
83 + <g filter="url(#glow)">
84 + <path d="M 396 400 L 628 400 L 396 624 L 628 624"
85 + fill="none" stroke="url(#emerald)" stroke-width="64"
86 + stroke-linecap="round" stroke-linejoin="round"/>
87 + </g>
88 + <!-- micro-highlights at the Z bends -->
89 + <g fill="#EAFFF6" filter="url(#softGlow)">
90 + <circle cx="628" cy="400" r="10"/>
91 + <circle cx="396" cy="624" r="10"/>
92 + </g>
93 + </g>
94 +</svg>
added assets/icon/zyquo-local-core.svg +89 −0
@@ -0,0 +1,89 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + zyquo-local-core.svg (variant B: Z-core — the Z as a circuit path)
4 + Zyquo Local
5 +
6 + Author: Simon-Pierre Boucher
7 + Mail: contact@spboucher.ai
8 +-->
9 +<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
10 + <defs>
11 + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
12 + <stop offset="0" stop-color="#1E2622"/>
13 + <stop offset="1" stop-color="#0F1412"/>
14 + </linearGradient>
15 + <linearGradient id="emerald" x1="0" y1="0" x2="0" y2="1" gradientUnits="userSpaceOnUse">
16 + <stop offset="280" stop-color="#17C787"/>
17 + <stop offset="744" stop-color="#0E9F6E"/>
18 + </linearGradient>
19 + <radialGradient id="coreGlow" cx="0.5" cy="0.47" r="0.55">
20 + <stop offset="0" stop-color="#17C787" stop-opacity="0.22"/>
21 + <stop offset="0.65" stop-color="#17C787" stop-opacity="0.05"/>
22 + <stop offset="1" stop-color="#17C787" stop-opacity="0"/>
23 + </radialGradient>
24 + <filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
25 + <feGaussianBlur in="SourceGraphic" stdDeviation="16" result="blur"/>
26 + <feMerge>
27 + <feMergeNode in="blur"/>
28 + <feMergeNode in="SourceGraphic"/>
29 + </feMerge>
30 + </filter>
31 + <filter id="softGlow" x="-60%" y="-60%" width="220%" height="220%">
32 + <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur"/>
33 + <feMerge>
34 + <feMergeNode in="blur"/>
35 + <feMergeNode in="SourceGraphic"/>
36 + </feMerge>
37 + </filter>
38 + <clipPath id="squircle">
39 + <rect x="100" y="100" width="824" height="824" rx="184" ry="184"/>
40 + </clipPath>
41 + </defs>
42 +
43 + <g clip-path="url(#squircle)">
44 + <rect x="100" y="100" width="824" height="824" fill="url(#bg)"/>
45 + <ellipse cx="512" cy="230" rx="540" ry="290" fill="#2A342E" opacity="0.30"/>
46 + <circle cx="512" cy="500" r="430" fill="url(#coreGlow)"/>
47 +
48 + <!-- faint circuit lattice in the field -->
49 + <g stroke="#28312C" stroke-width="6" stroke-linecap="round" fill="none">
50 + <path d="M 512 130 L 512 236"/>
51 + <path d="M 512 788 L 512 894"/>
52 + <path d="M 168 340 L 262 340 L 300 302"/>
53 + <path d="M 856 684 L 762 684 L 724 722"/>
54 + <path d="M 168 620 L 240 620"/>
55 + <path d="M 856 404 L 784 404"/>
56 + </g>
57 + <g fill="#333E38">
58 + <circle cx="512" cy="130" r="8"/>
59 + <circle cx="512" cy="894" r="8"/>
60 + <circle cx="168" cy="340" r="8"/>
61 + <circle cx="856" cy="684" r="8"/>
62 + <circle cx="168" cy="620" r="8"/>
63 + <circle cx="856" cy="404" r="8"/>
64 + </g>
65 +
66 + <!-- the Z as a luminous conductor trace -->
67 + <g filter="url(#glow)">
68 + <path d="M 330 300 L 694 300 L 330 724 L 694 724"
69 + fill="none" stroke="url(#emerald)" stroke-width="86"
70 + stroke-linecap="round" stroke-linejoin="round"/>
71 + </g>
72 +
73 + <!-- connection stubs off the Z ends, like a routed net -->
74 + <g stroke="#0E9F6E" stroke-width="18" stroke-linecap="round" opacity="0.85">
75 + <path d="M 330 300 L 236 300" fill="none"/>
76 + <path d="M 694 724 L 788 724" fill="none"/>
77 + </g>
78 +
79 + <!-- node dots at the bends + line ends -->
80 + <g fill="#EAFFF6" filter="url(#softGlow)">
81 + <circle cx="694" cy="300" r="13"/>
82 + <circle cx="330" cy="724" r="13"/>
83 + </g>
84 + <g fill="#BFF5DF" filter="url(#softGlow)">
85 + <circle cx="236" cy="300" r="11"/>
86 + <circle cx="788" cy="724" r="11"/>
87 + </g>
88 + </g>
89 +</svg>
added assets/icon/zyquo-local-simple.svg +31 −0
@@ -0,0 +1,31 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + zyquo-local-simple.svg — simplified variant baked for 16/32 px:
4 + traces and glow dropped, thicker Z, higher contrast.
5 +
6 + Zyquo Local
7 +
8 + Author: Simon-Pierre Boucher
9 + Mail: contact@spboucher.ai
10 +-->
11 +<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
12 + <defs>
13 + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
14 + <stop offset="0" stop-color="#222B26"/>
15 + <stop offset="1" stop-color="#101512"/>
16 + </linearGradient>
17 + <linearGradient id="emerald" x1="0" y1="0" x2="0" y2="1" gradientUnits="userSpaceOnUse">
18 + <stop offset="270" stop-color="#1CD693"/>
19 + <stop offset="760" stop-color="#0FA873"/>
20 + </linearGradient>
21 + <clipPath id="squircle">
22 + <rect x="100" y="100" width="824" height="824" rx="184" ry="184"/>
23 + </clipPath>
24 + </defs>
25 + <g clip-path="url(#squircle)">
26 + <rect x="100" y="100" width="824" height="824" fill="url(#bg)"/>
27 + <path d="M 320 290 L 704 290 L 320 734 L 704 734"
28 + fill="none" stroke="url(#emerald)" stroke-width="118"
29 + stroke-linecap="round" stroke-linejoin="round"/>
30 + </g>
31 +</svg>
added assets/icon/zyquo-local-template.svg +18 −0
@@ -0,0 +1,18 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + zyquo-local-template.svg — monochrome menu-bar template glyph (18×18 pt
4 + design, rendered @1x/@2x; NSImage isTemplate = true). Pure black shapes
5 + on transparency; macOS recolors it.
6 +
7 + Zyquo Local
8 +
9 + Author: Simon-Pierre Boucher
10 + Mail: contact@spboucher.ai
11 +-->
12 +<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
13 + <path d="M 300 250 L 724 250 L 300 774 L 724 774"
14 + fill="none" stroke="#000000" stroke-width="130"
15 + stroke-linecap="round" stroke-linejoin="round"/>
16 + <circle cx="724" cy="250" r="84" fill="#000000"/>
17 + <circle cx="300" cy="774" r="84" fill="#000000"/>
18 +</svg>
added assets/icon/zyquo-local.svg +98 −0
@@ -0,0 +1,98 @@
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + zyquo-local.svg — the Zyquo Local app icon (Z-core: the Z as a luminous
4 + circuit trace on deep graphite silicon). Source of truth for all raster
5 + sizes; chosen over the Z-chip exploration (kept in zyquo-local-chip.svg)
6 + for its far stronger small-size legibility.
7 +
8 + Zyquo Local
9 +
10 + Author: Simon-Pierre Boucher
11 + Mail: contact@spboucher.ai
12 +-->
13 +<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
14 + <defs>
15 + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
16 + <stop offset="0" stop-color="#1E2622"/>
17 + <stop offset="1" stop-color="#0F1412"/>
18 + </linearGradient>
19 + <linearGradient id="emerald" x1="0" y1="0" x2="0" y2="1" gradientUnits="userSpaceOnUse">
20 + <stop offset="280" stop-color="#17C787"/>
21 + <stop offset="744" stop-color="#0E9F6E"/>
22 + </linearGradient>
23 + <radialGradient id="topLight" cx="0.5" cy="0.18" r="0.75">
24 + <stop offset="0" stop-color="#2E3933" stop-opacity="0.85"/>
25 + <stop offset="0.55" stop-color="#2E3933" stop-opacity="0.25"/>
26 + <stop offset="1" stop-color="#2E3933" stop-opacity="0"/>
27 + </radialGradient>
28 + <radialGradient id="coreGlow" cx="0.5" cy="0.47" r="0.55">
29 + <stop offset="0" stop-color="#17C787" stop-opacity="0.22"/>
30 + <stop offset="0.65" stop-color="#17C787" stop-opacity="0.05"/>
31 + <stop offset="1" stop-color="#17C787" stop-opacity="0"/>
32 + </radialGradient>
33 + <filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
34 + <feGaussianBlur in="SourceGraphic" stdDeviation="16" result="blur"/>
35 + <feMerge>
36 + <feMergeNode in="blur"/>
37 + <feMergeNode in="SourceGraphic"/>
38 + </feMerge>
39 + </filter>
40 + <filter id="softGlow" x="-60%" y="-60%" width="220%" height="220%">
41 + <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur"/>
42 + <feMerge>
43 + <feMergeNode in="blur"/>
44 + <feMergeNode in="SourceGraphic"/>
45 + </feMerge>
46 + </filter>
47 + <clipPath id="squircle">
48 + <rect x="100" y="100" width="824" height="824" rx="184" ry="184"/>
49 + </clipPath>
50 + </defs>
51 +
52 + <g clip-path="url(#squircle)">
53 + <rect x="100" y="100" width="824" height="824" fill="url(#bg)"/>
54 + <rect x="100" y="100" width="824" height="824" fill="url(#topLight)"/>
55 + <circle cx="512" cy="500" r="430" fill="url(#coreGlow)"/>
56 +
57 + <!-- faint circuit lattice in the field -->
58 + <g stroke="#28312C" stroke-width="6" stroke-linecap="round" fill="none">
59 + <path d="M 512 130 L 512 236"/>
60 + <path d="M 512 788 L 512 894"/>
61 + <path d="M 168 340 L 262 340 L 300 302"/>
62 + <path d="M 856 684 L 762 684 L 724 722"/>
63 + <path d="M 168 620 L 240 620"/>
64 + <path d="M 856 404 L 784 404"/>
65 + </g>
66 + <g fill="#333E38">
67 + <circle cx="512" cy="130" r="8"/>
68 + <circle cx="512" cy="894" r="8"/>
69 + <circle cx="168" cy="340" r="8"/>
70 + <circle cx="856" cy="684" r="8"/>
71 + <circle cx="168" cy="620" r="8"/>
72 + <circle cx="856" cy="404" r="8"/>
73 + </g>
74 +
75 + <!-- the Z as a luminous conductor trace -->
76 + <g filter="url(#glow)">
77 + <path d="M 330 300 L 694 300 L 330 724 L 694 724"
78 + fill="none" stroke="url(#emerald)" stroke-width="86"
79 + stroke-linecap="round" stroke-linejoin="round"/>
80 + </g>
81 +
82 + <!-- connection stubs off the Z ends, like a routed net -->
83 + <g stroke="#0E9F6E" stroke-width="18" stroke-linecap="round" opacity="0.85">
84 + <path d="M 330 300 L 236 300" fill="none"/>
85 + <path d="M 694 724 L 788 724" fill="none"/>
86 + </g>
87 +
88 + <!-- node dots at the bends + line ends -->
89 + <g fill="#EAFFF6" filter="url(#softGlow)">
90 + <circle cx="694" cy="300" r="13"/>
91 + <circle cx="330" cy="724" r="13"/>
92 + </g>
93 + <g fill="#BFF5DF" filter="url(#softGlow)">
94 + <circle cx="236" cy="300" r="11"/>
95 + <circle cx="788" cy="724" r="11"/>
96 + </g>
97 + </g>
98 +</svg>
modified docs/PLAN.md +13 −1
@@ -86,7 +86,19 @@ ViewModels/, Views/ arrive with Phases 4/6).
86 86 **Checkpoint:** Zero warnings. UI layout contract for Phase 6 is §4.2 of the
87 87 project CLAUDE.md (exact spec); the design quality gate (§4.4) runs before
88 88 Definition of Done.
89 ## Phase 5 — App icon — pending
89 +## Phase 5 — App icon — ✅ DONE (2026-07-30)
90 +
91 +- [x] Both creative directions rendered and compared at 512/64 px: **Z-core chosen**
92 + (bold circuit-trace Z, far stronger small-size legibility); Z-chip kept as exploration
93 +- [x] Final zyquo-local.svg: graphite gradient squircle, radial top light (no banding),
94 + emerald-gradient Z trace with restrained glow, routed-net stubs, node micro-highlights
95 +- [x] Simplified 16/32 px variant (no traces/glow, thicker Z) — verified legible at 16 px
96 +- [x] Monochrome menu bar template (18/36 px PNGs)
97 +- [x] scripts/make-icon.sh: SVG → iconset (simple for ≤32, full for ≥64) → AppIcon.icns via iconutil
98 +- [x] Makefile bundles AppIcon.icns + menubar PNGs into Resources
99 +
100 +**Checkpoint:** AppIcon.icns (652 KB) generated; LOOKed at 512/64/32/16 renders and iterated
101 +(fixed vignette band edge). rsvg-convert installed via Homebrew per the spec's pipeline.
90 102 ## Phase 6 — Features / full UI — pending
91 103 ## Phase 7 — Local model verification — pending
92 104 ## Phase 8 — Signing & notarization — pending
added scripts/make-icon.sh +42 −0
@@ -0,0 +1,42 @@
1 +#!/bin/bash
2 +#
3 +# make-icon.sh
4 +# Zyquo Local
5 +#
6 +# Author: Simon-Pierre Boucher
7 +# Mail: contact@spboucher.ai
8 +#
9 +# SVG → AppIcon.icns + menu bar template PNGs.
10 +# Full-detail SVG for ≥64 px, simplified variant baked for 16/32 px.
11 +
12 +set -euo pipefail
13 +cd "$(dirname "$0")/../assets/icon"
14 +
15 +RSVG=rsvg-convert
16 +command -v $RSVG >/dev/null || { echo "rsvg-convert not found (brew install librsvg)"; exit 1; }
17 +
18 +ICONSET=AppIcon.iconset
19 +rm -rf "$ICONSET" && mkdir "$ICONSET"
20 +
21 +# 16/32 from the simplified variant (16@2x = 32 px also simplified)
22 +$RSVG -w 16 -h 16 zyquo-local-simple.svg -o "$ICONSET/icon_16x16.png"
23 +$RSVG -w 32 -h 32 zyquo-local-simple.svg -o "$ICONSET/icon_16x16@2x.png"
24 +$RSVG -w 32 -h 32 zyquo-local-simple.svg -o "$ICONSET/icon_32x32.png"
25 +# ≥64 from the full-detail icon
26 +$RSVG -w 64 -h 64 zyquo-local.svg -o "$ICONSET/icon_32x32@2x.png"
27 +$RSVG -w 128 -h 128 zyquo-local.svg -o "$ICONSET/icon_128x128.png"
28 +$RSVG -w 256 -h 256 zyquo-local.svg -o "$ICONSET/icon_128x128@2x.png"
29 +$RSVG -w 256 -h 256 zyquo-local.svg -o "$ICONSET/icon_256x256.png"
30 +$RSVG -w 512 -h 512 zyquo-local.svg -o "$ICONSET/icon_256x256@2x.png"
31 +$RSVG -w 512 -h 512 zyquo-local.svg -o "$ICONSET/icon_512x512.png"
32 +$RSVG -w 1024 -h 1024 zyquo-local.svg -o "$ICONSET/icon_512x512@2x.png"
33 +
34 +iconutil -c icns "$ICONSET" -o AppIcon.icns
35 +rm -rf "$ICONSET"
36 +
37 +# Menu bar template icon (18 pt @1x/@2x)
38 +mkdir -p menubar
39 +$RSVG -w 18 -h 18 zyquo-local-template.svg -o menubar/MenuBarIcon.png
40 +$RSVG -w 36 -h 36 zyquo-local-template.svg -o menubar/MenuBarIcon@2x.png
41 +
42 +echo "AppIcon.icns + menubar template generated."
43