From 5cb7ed33add895b4ebcc4c4c94819279c76ce062 Mon Sep 17 00:00:00 2001
From: pentaxis93 <13393192+pentaxis93@users.noreply.github.com>
Date: Tue, 10 Mar 2026 13:47:00 +0000
Subject: [PATCH 1/3] Add logo source files and generation script
---
assets/logo/generate.py | 174 ++++++++++++++++++++++++++++++++
assets/logo/groundwork-full.svg | 31 ++++++
assets/logo/groundwork-mark.svg | 11 ++
3 files changed, 216 insertions(+)
create mode 100644 assets/logo/generate.py
create mode 100644 assets/logo/groundwork-full.svg
create mode 100644 assets/logo/groundwork-mark.svg
diff --git a/assets/logo/generate.py b/assets/logo/generate.py
new file mode 100644
index 0000000..bab9159
--- /dev/null
+++ b/assets/logo/generate.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python3
+from __future__ import annotations
+
+import io
+import shutil
+import sys
+from pathlib import Path
+
+try:
+ import cairosvg
+ from PIL import Image, ImageDraw, ImageFont
+except ImportError:
+ print(
+ "Missing dependencies. Install with:\n"
+ " python3 -m venv /tmp/groundwork-logo-venv\n"
+ " /tmp/groundwork-logo-venv/bin/python -m pip install cairosvg Pillow",
+ file=sys.stderr,
+ )
+ raise SystemExit(1)
+
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+MARK_SVG = SCRIPT_DIR / "groundwork-mark.svg"
+FULL_SVG = SCRIPT_DIR / "groundwork-full.svg"
+DIST_DIR = SCRIPT_DIR / "dist"
+
+BACKGROUND = "#2A241C"
+TITLE_COLOR = "#BDA06A"
+TAGLINE_COLOR = "#9B7A3C"
+
+EXPECTED_OUTPUTS = [
+ "favicon.ico",
+ "favicon.svg",
+ "apple-touch-icon.png",
+ "logo-32.png",
+ "logo-64.png",
+ "logo-128.png",
+ "logo-256.png",
+ "logo-512.png",
+ "github-social-preview.png",
+ "og-image.png",
+ "avatar-500.png",
+]
+
+
+def require_sources() -> None:
+ missing = [str(path) for path in (MARK_SVG, FULL_SVG) if not path.exists()]
+ if missing:
+ raise FileNotFoundError(f"Missing source files: {', '.join(missing)}")
+
+
+def svg_to_png_bytes(svg_path: Path, width: int, height: int) -> bytes:
+ return cairosvg.svg2png(url=str(svg_path), output_width=width, output_height=height)
+
+
+def render_mark_image(size: int) -> Image.Image:
+ raw = svg_to_png_bytes(MARK_SVG, size, size)
+ return Image.open(io.BytesIO(raw)).convert("RGBA")
+
+
+def save_mark_png(filename: str, size: int) -> None:
+ target = DIST_DIR / filename
+ target.write_bytes(svg_to_png_bytes(MARK_SVG, size, size))
+
+
+def text_height(draw: ImageDraw.ImageDraw, text: str, font: ImageFont.ImageFont) -> int:
+ box = draw.textbbox((0, 0), text, font=font)
+ return box[3] - box[1]
+
+
+def text_center_x(draw: ImageDraw.ImageDraw, canvas_width: int, text: str, font: ImageFont.ImageFont) -> int:
+ box = draw.textbbox((0, 0), text, font=font)
+ text_width = box[2] - box[0]
+ return (canvas_width - text_width) // 2
+
+
+def load_font(size: int, bold: bool = False) -> ImageFont.ImageFont:
+ candidates = (
+ ["/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", "/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf"]
+ if bold
+ else ["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", "/usr/share/fonts/dejavu/DejaVuSans.ttf"]
+ )
+ for candidate in candidates:
+ path = Path(candidate)
+ if path.exists():
+ return ImageFont.truetype(str(path), size=size)
+ return ImageFont.load_default()
+
+
+def save_social_like(filename: str, width: int, height: int) -> None:
+ image = Image.new("RGB", (width, height), BACKGROUND)
+ draw = ImageDraw.Draw(image)
+
+ title = "groundwork"
+ tagline = "agentic development methodology"
+
+ title_font = load_font(max(48, int(height * 0.115)), bold=True)
+ tagline_font = load_font(max(24, int(height * 0.05)), bold=False)
+
+ mark_size = int(min(width, height) * 0.24)
+ gap_mark_title = int(height * 0.07)
+ gap_title_tagline = int(height * 0.03)
+
+ title_h = text_height(draw, title, title_font)
+ tagline_h = text_height(draw, tagline, tagline_font)
+ block_height = mark_size + gap_mark_title + title_h + gap_title_tagline + tagline_h
+ top = (height - block_height) // 2
+
+ mark = render_mark_image(mark_size)
+ mark_x = (width - mark_size) // 2
+ image.paste(mark, (mark_x, top), mark)
+
+ title_y = top + mark_size + gap_mark_title
+ draw.text(
+ (text_center_x(draw, width, title, title_font), title_y),
+ title,
+ font=title_font,
+ fill=TITLE_COLOR,
+ )
+
+ tagline_y = title_y + title_h + gap_title_tagline
+ draw.text(
+ (text_center_x(draw, width, tagline, tagline_font), tagline_y),
+ tagline,
+ font=tagline_font,
+ fill=TAGLINE_COLOR,
+ )
+
+ image.save(DIST_DIR / filename, format="PNG")
+
+
+def save_avatar() -> None:
+ size = 500
+ image = Image.new("RGB", (size, size), BACKGROUND)
+ mark_size = 280
+ mark = render_mark_image(mark_size)
+ mark_x = (size - mark_size) // 2
+ mark_y = (size - mark_size) // 2
+ image.paste(mark, (mark_x, mark_y), mark)
+ image.save(DIST_DIR / "avatar-500.png", format="PNG")
+
+
+def save_favicon_ico() -> None:
+ icon = render_mark_image(32)
+ icon.save(DIST_DIR / "favicon.ico", format="ICO", sizes=[(32, 32)])
+
+
+def main() -> int:
+ require_sources()
+ DIST_DIR.mkdir(parents=True, exist_ok=True)
+
+ shutil.copyfile(MARK_SVG, DIST_DIR / "favicon.svg")
+ save_favicon_ico()
+
+ save_mark_png("apple-touch-icon.png", 180)
+ for size in (32, 64, 128, 256, 512):
+ save_mark_png(f"logo-{size}.png", size)
+
+ save_social_like("github-social-preview.png", 1280, 640)
+ save_social_like("og-image.png", 1200, 630)
+ save_avatar()
+
+ missing = [name for name in EXPECTED_OUTPUTS if not (DIST_DIR / name).exists()]
+ if missing:
+ raise RuntimeError(f"Generation incomplete. Missing: {', '.join(missing)}")
+
+ print("Generated assets:")
+ for name in EXPECTED_OUTPUTS:
+ print(f"- {DIST_DIR / name}")
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/assets/logo/groundwork-full.svg b/assets/logo/groundwork-full.svg
new file mode 100644
index 0000000..20d914f
--- /dev/null
+++ b/assets/logo/groundwork-full.svg
@@ -0,0 +1,31 @@
+
+
diff --git a/assets/logo/groundwork-mark.svg b/assets/logo/groundwork-mark.svg
new file mode 100644
index 0000000..e9e84c4
--- /dev/null
+++ b/assets/logo/groundwork-mark.svg
@@ -0,0 +1,11 @@
+
+
From 694625c00f1f97231096877e6998b32c8281360e Mon Sep 17 00:00:00 2001
From: pentaxis93 <13393192+pentaxis93@users.noreply.github.com>
Date: Tue, 10 Mar 2026 13:47:34 +0000
Subject: [PATCH 2/3] Gitignore derived logo assets
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 71b9ca3..c1c1382 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
.codex/
.groundwork/
.issues/
+assets/logo/dist/
From 95e5056ef92cb5bed244e85afa91ef0a5b352a42 Mon Sep 17 00:00:00 2001
From: pentaxis93 <13393192+pentaxis93@users.noreply.github.com>
Date: Tue, 10 Mar 2026 13:50:16 +0000
Subject: [PATCH 3/3] Document logo asset generation workflow
---
README.md | 11 +++++++++++
assets/logo/README.md | 38 ++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 assets/logo/README.md
diff --git a/README.md b/README.md
index a4f09da..aace5bf 100644
--- a/README.md
+++ b/README.md
@@ -83,6 +83,17 @@ Issue sync troubleshooting:
`gh-issue-sync pull`
`gh-issue-sync status`
+## Logo Assets
+
+Logo sources and generation tooling are under [`assets/logo/`](assets/logo/).
+Regenerate derived branding assets from repo root with:
+
+```bash
+python3 assets/logo/generate.py
+```
+
+See [`assets/logo/README.md`](assets/logo/README.md) for dependency setup and output details.
+
## Project Layout
```
diff --git a/assets/logo/README.md b/assets/logo/README.md
new file mode 100644
index 0000000..d7d2aa5
--- /dev/null
+++ b/assets/logo/README.md
@@ -0,0 +1,38 @@
+# Logo Assets
+
+Canonical sources live in this directory:
+
+- `groundwork-mark.svg`
+- `groundwork-full.svg`
+
+Derived assets are generated into `assets/logo/dist/` and are intentionally not committed.
+
+## Regenerate
+
+From the repo root:
+
+```bash
+python3 assets/logo/generate.py
+```
+
+If dependencies are missing, use a virtual environment:
+
+```bash
+python3 -m venv /tmp/groundwork-logo-venv
+/tmp/groundwork-logo-venv/bin/python -m pip install cairosvg Pillow
+/tmp/groundwork-logo-venv/bin/python assets/logo/generate.py
+```
+
+## Generated Outputs
+
+- `dist/favicon.ico`
+- `dist/favicon.svg`
+- `dist/apple-touch-icon.png`
+- `dist/logo-32.png`
+- `dist/logo-64.png`
+- `dist/logo-128.png`
+- `dist/logo-256.png`
+- `dist/logo-512.png`
+- `dist/github-social-preview.png`
+- `dist/og-image.png`
+- `dist/avatar-500.png`