diff --git a/assets/social-templates-829/PR_SUBMISSION.md b/assets/social-templates-829/PR_SUBMISSION.md new file mode 100644 index 000000000..1e6704146 --- /dev/null +++ b/assets/social-templates-829/PR_SUBMISSION.md @@ -0,0 +1,25 @@ +# PR Title + +feat: add SolFoundry social media templates + +# PR Body + +Closes #829 + +## Summary + +- Adds 5 reusable SolFoundry bounty announcement templates +- Exports each template as 1080x1080 feed PNG and 1200x675 Twitter/X card PNG +- Includes editable SVG source files for both formats +- Includes source JSON plus a reproducible renderer for future copy/palette updates + +## Verification + +- Generated all PNG exports with `assets/social-templates-829/source/render_templates.py` +- Verified 10 PNG exports exist across feed and Twitter/X card formats +- Verified expected dimensions: 1080x1080 and 1200x675 +- Ran `git diff --check` + +## Reward + +FNDRY wallet: `` diff --git a/assets/social-templates-829/README.md b/assets/social-templates-829/README.md new file mode 100644 index 000000000..4b01bf8a4 --- /dev/null +++ b/assets/social-templates-829/README.md @@ -0,0 +1,34 @@ +# SolFoundry Social Media Templates + +Submission for issue #829: Social Media Templates. + +## Deliverables + +- 5 reusable bounty announcement template concepts +- Each template exported in: + - 1080 x 1080 feed PNG + - 1200 x 675 Twitter/X card PNG +- Editable source assets included as SVG files +- Text fields are intentionally structured around bounty title, reward amount, and tier + +## Files + +- `png/feed/`: square feed exports +- `png/twitter-card/`: Twitter/X card exports +- `svg/feed/`: editable square SVG source files +- `svg/twitter-card/`: editable card SVG source files +- `source/template_specs.json`: editable template copy and palette data +- `source/render_templates.py`: reproducible renderer for the PNG exports +- `preview-contact-sheet.png`: quick visual overview + +## Template Set + +1. Bounty Open +2. High Reward +3. Audit Call +4. Builder Spotlight +5. Launch Week + +## Brand Notes + +The set uses a dark forge-themed base with SolFoundry emerald, magenta, violet, cyan, and gold accents. Each layout keeps a clear editable area for future bounty titles, reward amounts, and tier labels. diff --git a/assets/social-templates-829/png/feed/audit-call.png b/assets/social-templates-829/png/feed/audit-call.png new file mode 100644 index 000000000..45fb39012 Binary files /dev/null and b/assets/social-templates-829/png/feed/audit-call.png differ diff --git a/assets/social-templates-829/png/feed/bounty-open.png b/assets/social-templates-829/png/feed/bounty-open.png new file mode 100644 index 000000000..ae1f4bf5a Binary files /dev/null and b/assets/social-templates-829/png/feed/bounty-open.png differ diff --git a/assets/social-templates-829/png/feed/builder-spotlight.png b/assets/social-templates-829/png/feed/builder-spotlight.png new file mode 100644 index 000000000..146f3a42f Binary files /dev/null and b/assets/social-templates-829/png/feed/builder-spotlight.png differ diff --git a/assets/social-templates-829/png/feed/high-reward.png b/assets/social-templates-829/png/feed/high-reward.png new file mode 100644 index 000000000..8766bca06 Binary files /dev/null and b/assets/social-templates-829/png/feed/high-reward.png differ diff --git a/assets/social-templates-829/png/feed/launch-week.png b/assets/social-templates-829/png/feed/launch-week.png new file mode 100644 index 000000000..547a86d7e Binary files /dev/null and b/assets/social-templates-829/png/feed/launch-week.png differ diff --git a/assets/social-templates-829/png/twitter-card/audit-call.png b/assets/social-templates-829/png/twitter-card/audit-call.png new file mode 100644 index 000000000..1fbfa071f Binary files /dev/null and b/assets/social-templates-829/png/twitter-card/audit-call.png differ diff --git a/assets/social-templates-829/png/twitter-card/bounty-open.png b/assets/social-templates-829/png/twitter-card/bounty-open.png new file mode 100644 index 000000000..67e5a91c4 Binary files /dev/null and b/assets/social-templates-829/png/twitter-card/bounty-open.png differ diff --git a/assets/social-templates-829/png/twitter-card/builder-spotlight.png b/assets/social-templates-829/png/twitter-card/builder-spotlight.png new file mode 100644 index 000000000..a43f317e5 Binary files /dev/null and b/assets/social-templates-829/png/twitter-card/builder-spotlight.png differ diff --git a/assets/social-templates-829/png/twitter-card/high-reward.png b/assets/social-templates-829/png/twitter-card/high-reward.png new file mode 100644 index 000000000..e7ecd902f Binary files /dev/null and b/assets/social-templates-829/png/twitter-card/high-reward.png differ diff --git a/assets/social-templates-829/png/twitter-card/launch-week.png b/assets/social-templates-829/png/twitter-card/launch-week.png new file mode 100644 index 000000000..49b7ce7b7 Binary files /dev/null and b/assets/social-templates-829/png/twitter-card/launch-week.png differ diff --git a/assets/social-templates-829/preview-contact-sheet.png b/assets/social-templates-829/preview-contact-sheet.png new file mode 100644 index 000000000..55aae7d82 Binary files /dev/null and b/assets/social-templates-829/preview-contact-sheet.png differ diff --git a/assets/social-templates-829/source/render_templates.py b/assets/social-templates-829/source/render_templates.py new file mode 100644 index 000000000..bb9c78740 --- /dev/null +++ b/assets/social-templates-829/source/render_templates.py @@ -0,0 +1,208 @@ +import json +import math +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFilter, ImageFont + + +ROOT = Path(__file__).resolve().parents[1] +SOURCE_DIR = ROOT / "source" +PNG_DIR = ROOT / "png" +SVG_DIR = ROOT / "svg" + +FONT_BOLD = Path("C:/Windows/Fonts/arialbd.ttf") +FONT_REGULAR = Path("C:/Windows/Fonts/arial.ttf") + +FORMATS = { + "feed": (1080, 1080), + "twitter-card": (1200, 675), +} + + +def color(hex_value, alpha=255): + hex_value = hex_value.strip("#") + return tuple(int(hex_value[i : i + 2], 16) for i in (0, 2, 4)) + (alpha,) + + +def font(size, bold=True): + path = FONT_BOLD if bold else FONT_REGULAR + return ImageFont.truetype(str(path), size) + + +def fit_font(text, max_width, start_size, min_size=24): + size = start_size + scratch = ImageDraw.Draw(Image.new("RGBA", (1, 1))) + while size >= min_size: + candidate = font(size) + left, top, right, bottom = scratch.textbbox((0, 0), text, font=candidate) + if right - left <= max_width: + return candidate + size -= 2 + return font(min_size) + + +def draw_centered(draw, xy, text, fill, size, max_width): + x, y = xy + fnt = fit_font(text, max_width, size) + left, top, right, bottom = draw.textbbox((0, 0), text, font=fnt) + draw.text((x - (right - left) / 2, y), text, font=fnt, fill=fill) + + +def rounded(draw, rect, radius, fill, outline=None, width=1): + draw.rounded_rectangle(rect, radius=radius, fill=fill, outline=outline, width=width) + + +def glow_line(img, points, fill, width): + glow = Image.new("RGBA", img.size, (0, 0, 0, 0)) + gd = ImageDraw.Draw(glow) + gd.line(points, fill=fill, width=width * 4, joint="curve") + img.alpha_composite(glow.filter(ImageFilter.GaussianBlur(width * 2))) + ImageDraw.Draw(img).line(points, fill=fill, width=width, joint="curve") + + +def draw_anvil_mark(draw, x, y, scale, accent, secondary): + body = [ + (x - 58 * scale, y + 22 * scale), + (x - 35 * scale, y - 22 * scale), + (x + 42 * scale, y - 22 * scale), + (x + 64 * scale, y + 22 * scale), + (x + 38 * scale, y + 52 * scale), + (x - 44 * scale, y + 52 * scale), + ] + draw.polygon(body, fill=accent) + rounded(draw, (x - 42 * scale, y - 44 * scale, x + 48 * scale, y - 20 * scale), int(7 * scale), secondary) + draw.polygon([(x - 42 * scale, y - 38 * scale), (x - 86 * scale, y - 30 * scale), (x - 42 * scale, y - 19 * scale)], fill=accent) + rounded(draw, (x - 34 * scale, y + 54 * scale, x + 40 * scale, y + 72 * scale), int(6 * scale), secondary) + + +def draw_background(img, spec): + w, h = img.size + draw = ImageDraw.Draw(img) + for y in range(h): + t = y / max(1, h - 1) + r = int(8 + 10 * t) + g = int(10 + 8 * t) + b = int(22 + 34 * t) + draw.line((0, y, w, y), fill=(r, g, b, 255)) + + accent = color(spec["accent"], 210) + secondary = color(spec["secondary"], 190) + for i in range(7): + angle = math.radians(i * 51) + cx = int(w * 0.5 + math.cos(angle) * w * 0.33) + cy = int(h * 0.47 + math.sin(angle) * h * 0.32) + draw.ellipse((cx - 5, cy - 5, cx + 5, cy + 5), fill=accent if i % 2 == 0 else secondary) + if i: + prev_angle = math.radians((i - 1) * 51) + px = int(w * 0.5 + math.cos(prev_angle) * w * 0.33) + py = int(h * 0.47 + math.sin(prev_angle) * h * 0.32) + draw.line((px, py, cx, cy), fill=(255, 255, 255, 38), width=2) + + glow_line(img, [(int(w * 0.07), int(h * 0.78)), (int(w * 0.32), int(h * 0.64)), (int(w * 0.56), int(h * 0.82)), (int(w * 0.92), int(h * 0.58))], color(spec["accent"], 170), 5) + + +def render_png(spec, fmt, size): + w, h = size + img = Image.new("RGBA", size, (0, 0, 0, 255)) + draw_background(img, spec) + draw = ImageDraw.Draw(img) + accent = color(spec["accent"]) + secondary = color(spec["secondary"]) + + pad = int(min(w, h) * 0.065) + rounded(draw, (pad, pad, w - pad, h - pad), int(min(w, h) * 0.055), (255, 255, 255, 235)) + rounded(draw, (pad + 10, pad + 10, w - pad - 10, h - pad - 10), int(min(w, h) * 0.047), (8, 10, 22, 248), accent, 3) + + draw_anvil_mark(draw, pad + 105, pad + 92, 0.85, accent, secondary) + draw.text((pad + 178, pad + 50), "SolFoundry", font=font(34), fill=(238, 250, 255, 245)) + draw.text((pad + 178, pad + 90), "Bounty announcement template", font=font(20, False), fill=(170, 186, 205, 240)) + + pill_w = 260 if fmt == "feed" else 300 + rounded(draw, (w - pad - pill_w, pad + 44, w - pad - 28, pad + 92), 24, accent) + draw_centered(draw, (w - pad - pill_w / 2 - 14, pad + 52), spec["tier"], (8, 10, 22, 255), 22, pill_w - 40) + + content_x = pad + 70 + title_y = int(h * (0.29 if fmt == "feed" else 0.30)) + draw.text((content_x, title_y), spec["eyebrow"], font=font(30), fill=accent) + draw.text((content_x, title_y + 48), spec["title"], font=fit_font(spec["title"], int(w * 0.72), 78 if fmt == "feed" else 68), fill=(255, 255, 255, 255)) + + box_top = int(h * (0.61 if fmt == "feed" else 0.62)) + box_height = int(h * 0.18) + rounded(draw, (content_x, box_top, w - pad - 70, box_top + box_height), 24, (13, 18, 34, 245), secondary, 3) + draw.text((content_x + 34, box_top + 28), "REWARD", font=font(22), fill=(170, 186, 205, 255)) + draw.text((content_x + 34, box_top + 62), spec["reward"], font=fit_font(spec["reward"], int(w * 0.47), 52), fill=secondary) + draw.text((w - pad - 280, box_top + 44), "Apply on GitHub", font=font(26), fill=(255, 255, 255, 245)) + draw.text((w - pad - 280, box_top + 82), "Open race | Builder friendly", font=font(20, False), fill=(170, 186, 205, 240)) + + footer_y = h - pad - 72 + draw.text((content_x, footer_y), "Replace title, reward, and tier for future bounty posts.", font=font(22, False), fill=(194, 208, 224, 245)) + rounded(draw, (w - pad - 210, footer_y - 8, w - pad - 48, footer_y + 42), 22, (255, 255, 255, 235)) + draw_centered(draw, (w - pad - 129, footer_y), "$FNDRY", (8, 10, 22, 255), 24, 140) + + out = PNG_DIR / fmt / f"{spec['slug']}.png" + out.parent.mkdir(parents=True, exist_ok=True) + img.save(out) + return out + + +def render_svg(spec, fmt, size): + w, h = size + svg = f""" + + + + + + + + + + + + + SolFoundry + {spec['eyebrow']} + {spec['title']} + + REWARD + {spec['reward']} + {spec['tier']} + Editable SVG source: replace title, reward, and tier text. + +""" + out = SVG_DIR / fmt / f"{spec['slug']}.svg" + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(svg, encoding="utf-8") + return out + + +def build_contact_sheet(files): + thumbs = [] + for file in files: + im = Image.open(file) + im.thumbnail((260, 180)) + thumbs.append((file, im.copy())) + sheet = Image.new("RGBA", (5 * 300, 2 * 230), (18, 22, 32, 255)) + draw = ImageDraw.Draw(sheet) + for i, (file, im) in enumerate(thumbs[:10]): + x = (i % 5) * 300 + 20 + y = (i // 5) * 230 + 18 + sheet.alpha_composite(im, (x, y)) + draw.text((x, y + 184), file.parent.name + "/" + file.stem, fill=(235, 248, 255, 255)) + sheet.save(ROOT / "preview-contact-sheet.png") + + +def main(): + specs = json.loads((SOURCE_DIR / "template_specs.json").read_text(encoding="utf-8")) + png_files = [] + for spec in specs: + for fmt, size in FORMATS.items(): + png_files.append(render_png(spec, fmt, size)) + render_svg(spec, fmt, size) + build_contact_sheet(png_files) + for file in png_files: + print(file) + + +if __name__ == "__main__": + main() diff --git a/assets/social-templates-829/source/template_specs.json b/assets/social-templates-829/source/template_specs.json new file mode 100644 index 000000000..2e0aba58d --- /dev/null +++ b/assets/social-templates-829/source/template_specs.json @@ -0,0 +1,47 @@ +[ + { + "slug": "bounty-open", + "eyebrow": "NEW BOUNTY", + "title": "Bounty Title Goes Here", + "reward": "150,000 FNDRY", + "tier": "T1 OPEN RACE", + "accent": "#14F195", + "secondary": "#9945FF" + }, + { + "slug": "high-reward", + "eyebrow": "HIGH REWARD", + "title": "Ship The Next Forge Upgrade", + "reward": "500,000 FNDRY", + "tier": "T2 FRONTEND", + "accent": "#FF4FD8", + "secondary": "#14F195" + }, + { + "slug": "audit-call", + "eyebrow": "SECURITY REVIEW", + "title": "Audit A Critical Contract", + "reward": "300,000 FNDRY", + "tier": "T2 SECURITY", + "accent": "#FFD166", + "secondary": "#9945FF" + }, + { + "slug": "builder-spotlight", + "eyebrow": "BUILDER SPOTLIGHT", + "title": "Top Contributor Wanted", + "reward": "100,000 FNDRY", + "tier": "T1 CREATIVE", + "accent": "#6EE7FF", + "secondary": "#14F195" + }, + { + "slug": "launch-week", + "eyebrow": "LAUNCH WEEK", + "title": "Help Forge The Next Release", + "reward": "250,000 FNDRY", + "tier": "T1 FAST TRACK", + "accent": "#9945FF", + "secondary": "#FF4FD8" + } +] diff --git a/assets/social-templates-829/svg/feed/audit-call.svg b/assets/social-templates-829/svg/feed/audit-call.svg new file mode 100644 index 000000000..582bfc20f --- /dev/null +++ b/assets/social-templates-829/svg/feed/audit-call.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + SECURITY REVIEW + Audit A Critical Contract + + REWARD + 300,000 FNDRY + T2 SECURITY + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/feed/bounty-open.svg b/assets/social-templates-829/svg/feed/bounty-open.svg new file mode 100644 index 000000000..360a3e21c --- /dev/null +++ b/assets/social-templates-829/svg/feed/bounty-open.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + NEW BOUNTY + Bounty Title Goes Here + + REWARD + 150,000 FNDRY + T1 OPEN RACE + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/feed/builder-spotlight.svg b/assets/social-templates-829/svg/feed/builder-spotlight.svg new file mode 100644 index 000000000..ba04cb104 --- /dev/null +++ b/assets/social-templates-829/svg/feed/builder-spotlight.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + BUILDER SPOTLIGHT + Top Contributor Wanted + + REWARD + 100,000 FNDRY + T1 CREATIVE + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/feed/high-reward.svg b/assets/social-templates-829/svg/feed/high-reward.svg new file mode 100644 index 000000000..1830269f4 --- /dev/null +++ b/assets/social-templates-829/svg/feed/high-reward.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + HIGH REWARD + Ship The Next Forge Upgrade + + REWARD + 500,000 FNDRY + T2 FRONTEND + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/feed/launch-week.svg b/assets/social-templates-829/svg/feed/launch-week.svg new file mode 100644 index 000000000..af4fcbc0a --- /dev/null +++ b/assets/social-templates-829/svg/feed/launch-week.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + LAUNCH WEEK + Help Forge The Next Release + + REWARD + 250,000 FNDRY + T1 FAST TRACK + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/twitter-card/audit-call.svg b/assets/social-templates-829/svg/twitter-card/audit-call.svg new file mode 100644 index 000000000..dea56b594 --- /dev/null +++ b/assets/social-templates-829/svg/twitter-card/audit-call.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + SECURITY REVIEW + Audit A Critical Contract + + REWARD + 300,000 FNDRY + T2 SECURITY + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/twitter-card/bounty-open.svg b/assets/social-templates-829/svg/twitter-card/bounty-open.svg new file mode 100644 index 000000000..fefb132e0 --- /dev/null +++ b/assets/social-templates-829/svg/twitter-card/bounty-open.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + NEW BOUNTY + Bounty Title Goes Here + + REWARD + 150,000 FNDRY + T1 OPEN RACE + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/twitter-card/builder-spotlight.svg b/assets/social-templates-829/svg/twitter-card/builder-spotlight.svg new file mode 100644 index 000000000..315ac7fdf --- /dev/null +++ b/assets/social-templates-829/svg/twitter-card/builder-spotlight.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + BUILDER SPOTLIGHT + Top Contributor Wanted + + REWARD + 100,000 FNDRY + T1 CREATIVE + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/twitter-card/high-reward.svg b/assets/social-templates-829/svg/twitter-card/high-reward.svg new file mode 100644 index 000000000..fe9c1e384 --- /dev/null +++ b/assets/social-templates-829/svg/twitter-card/high-reward.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + HIGH REWARD + Ship The Next Forge Upgrade + + REWARD + 500,000 FNDRY + T2 FRONTEND + Editable SVG source: replace title, reward, and tier text. + diff --git a/assets/social-templates-829/svg/twitter-card/launch-week.svg b/assets/social-templates-829/svg/twitter-card/launch-week.svg new file mode 100644 index 000000000..a41852b8f --- /dev/null +++ b/assets/social-templates-829/svg/twitter-card/launch-week.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + SolFoundry + LAUNCH WEEK + Help Forge The Next Release + + REWARD + 250,000 FNDRY + T1 FAST TRACK + Editable SVG source: replace title, reward, and tier text. +