Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added marketing/MarkupScreenshots-ipad/IMG_0200.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added marketing/MarkupScreenshots-ipad/IMG_0202.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added marketing/MarkupScreenshots-ipad/IMG_0203.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added marketing/MarkupScreenshots-ipad/IMG_0204.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added marketing/MarkupScreenshots-ipad/IMG_0205.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 7 additions & 6 deletions marketing/app-store/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ for the full step-by-step (certs, ASC, submit).
- **[screenshots/ios-6.9/](./screenshots/ios-6.9/)** — 5 ready-to-upload iPhone
6.9″ marketing screenshots (1320×2868).
- **[screenshots/README.md](./screenshots/README.md)** — caption map, sizes, and
what's still needed (iPad). Source captures: `../MarkupScreenshots/`.
caption maps + sizes. Source captures: `../MarkupScreenshots-{iphone,ipad,mac}/`.
- Regenerate screenshots: `python3 marketing/scripts/make-screenshots.py`.

## iOS — status
Expand All @@ -29,7 +29,7 @@ for the full step-by-step (certs, ASC, submit).
| Promo / keywords / description | ✅ in [listing.md](./listing.md) |
| App Privacy label | ✅ "Data Not Collected" ([privacy-and-review.md](./privacy-and-review.md)) |
| iPhone 6.9″ screenshots | ✅ 5 in [screenshots/ios-6.9/](./screenshots/ios-6.9/) |
| **iPad 13″ screenshots** | ❌ still needed — the app is universal, so Apple requires them |
| iPad 13″ screenshots | ✅ 5 in [screenshots/ipad-13/](./screenshots/ipad-13/) |
| Submit for Review | 🔑 you, in App Store Connect |

## To finish iOS (in App Store Connect)
Expand All @@ -41,7 +41,8 @@ for the full step-by-step (certs, ASC, submit).
6. **Submit for Review.**

## Mac
Listing copy for the Mac app is in [listing.md](./listing.md) (§Mac). Mac
screenshots aren't in this kit yet (the provided captures are iPhone) — capture
macOS shots (editor, GitHub vault, search, themes) and they can be framed the
same way via the generator script.
Listing copy for the Mac app is in [listing.md](./listing.md) (§Mac); **5 Mac
screenshots** are in [screenshots/mac/](./screenshots/mac/) (2880×1800). Create
the Mac App Store record (see the launch checklist), paste the §Mac copy, upload
these screenshots, and submit. (Optional: add a Mac "Open from GitHub" shot — the
set is currently all local-folder docs.)
67 changes: 43 additions & 24 deletions marketing/app-store/screenshots/README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,51 @@
# Screenshots

Marketing screenshots for the App Store, composed from the raw device captures in
`../../MarkupScreenshots/` by `marketing/scripts/make-screenshots.py` (brand
gradient + caption + framed screenshot; iOS status bar / TestFlight banner cropped).
App Store marketing screenshots, composed from the raw captures in
`../../MarkupScreenshots*/` by `marketing/scripts/make-screenshots.py` (brand
gradient + wordmark + caption + the screenshot framed with shadow/rim). iOS status
bars / TestFlight banners are cropped off.

Regenerate: `python3 marketing/scripts/make-screenshots.py`

## iOS — `ios-6.9/` (1320×2868, "6.9-inch display")
This is Apple's current primary iPhone size; ASC scales it to the smaller iPhone
slots, so a separate 6.5″ set is optional.
## iPhone — `ios-6.9/` (1320×2868, "6.9-inch display")
Apple's primary iPhone size; ASC scales it down to the smaller iPhone slots.

| # | File | Headline | Source |
|---|------|----------|--------|
| 1 | `01_read.png` | Read Markdown, beautifully rendered | IMG_4042 (reader + code + tabs) |
| 2 | `02_github-vault.png` | Open any GitHub repo as a vault | IMG_4041 (repo file list) |
| 3 | `03_bring-your-own.png` | Bring your own folder or GitHub repo | IMG_4039 (onboarding) |
| 4 | `04_reader-first.png` | A reader-first Markdown app | IMG_4038 (onboarding) |
| 5 | `05_files-icloud.png` | Works with Files & iCloud Drive | IMG_4040 (Files picker) |

Upload at least 1–3; the first 2–3 appear in App Store search results, so lead
with `01` and `02`. `05` (the system Files picker) is the weakest — optional.

## Still needed
- **iPad 13″ (2064×2752)** — the app is universal, so Apple **requires** iPad
screenshots. Capture a few on an iPad (or iPad simulator) — e.g. tabs + split
live preview, the file list beside the reader — drop the raw PNGs in
`../../MarkupScreenshots/`, add them to `SHOTS` in the generator with an
`ipad-13` output dir, and re-run.
- **Mac (1280×800 or 2560×1600)** — for the Mac App Store listing; capture the
desktop app (editor, GitHub vault, search, HTML export, themes).
- Optional: a localized 简体中文 set (re-run with 中文 captions).
| 1 | `01_read.png` | Read Markdown, beautifully rendered | IMG_4042 |
| 2 | `02_github-vault.png` | Open any GitHub repo as a vault | IMG_4041 |
| 3 | `03_bring-your-own.png` | Bring your own folder or GitHub repo | IMG_4039 |
| 4 | `04_reader-first.png` | A reader-first Markdown app | IMG_4038 |
| 5 | `05_files-icloud.png` | Works with Files & iCloud Drive | IMG_4040 |

## iPad — `ipad-13/` (2752×2064, "13-inch display", landscape)
Required for the universal app. Landscape is accepted by ASC.

| # | File | Headline | Source |
|---|------|----------|--------|
| 1 | `01_split-read.png` | Read your vault on iPad | IMG_0203 |
| 2 | `02_github-vault.png` | Open any GitHub repo as a vault | IMG_0202 |
| 3 | `03_themes.png` | Light, dark & sepia | IMG_0204 |
| 4 | `04_refresh.png` | Pull the latest from GitHub | IMG_0205 |
| 5 | `05_reader-first.png` | A reader-first Markdown app | IMG_0200 |

## Mac — `mac/` (2880×1800)
For the Mac App Store listing.

| # | File | Headline | Source |
|---|------|----------|--------|
| 1 | `01_reader.png` | Your Markdown, beautifully rendered | 205806 |
| 2 | `02_markdown.png` | Plain Markdown, always | 210215 |
| 3 | `03_quick-open.png` | Jump to any file | 210532 |
| 4 | `04_commands.png` | A command for everything | 205855 |
| 5 | `05_navigate.png` | Navigate big docs with ease | 210149 (cropped) |

Notes:
- **`05_navigate`** is cropped to the app window because the raw `210149` had a
Chrome window behind it; the crop shows the reader + outline rail (no file
sidebar). For a cleaner shot, re-capture that view with no other window behind.
- The Mac set has **no GitHub-vault shot** (all raws were a local-folder doc).
Optional nice-to-have: capture the Mac "Open from GitHub" flow and add it.

## Optional
- A localized **简体中文** set (re-run with 中文 captions in the script).
Binary file modified marketing/app-store/screenshots/ios-6.9/01_read.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified marketing/app-store/screenshots/ios-6.9/02_github-vault.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified marketing/app-store/screenshots/ios-6.9/03_bring-your-own.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified marketing/app-store/screenshots/ios-6.9/04_reader-first.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified marketing/app-store/screenshots/ios-6.9/05_files-icloud.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
177 changes: 103 additions & 74 deletions marketing/scripts/make-screenshots.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,32 @@
#!/usr/bin/env python3
"""Compose App Store marketing screenshots for Markup (iOS).
"""Compose App Store marketing screenshots for Markup (iOS + iPad + macOS).

Takes the raw device captures in marketing/MarkupScreenshots/ and renders them
onto branded 1320×2868 canvases (App Store "6.9-inch display" size) with a
headline caption, a subline, and the screenshot framed with rounded corners +
a soft shadow. The iOS status bar / TestFlight banner is cropped off the top.
Frames the raw device/app captures onto branded gradient canvases at the App
Store sizes, with a wordmark, a headline, a subline, and the screenshot framed
with rounded corners + a soft shadow + a hairline rim. Portrait (iPhone) and
landscape (iPad / Mac) are handled by the same composer — the screenshot is
scaled to fit the area below the caption.

Run: python3 marketing/scripts/make-screenshots.py
Out: marketing/app-store/screenshots/ios-6.9/NN_slug.png
Out: marketing/app-store/screenshots/{ios-6.9,ipad-13,mac}/NN_slug.png
"""
import os
from PIL import Image, ImageDraw, ImageFont, ImageFilter

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(os.path.dirname(HERE)) # repo root
SRC = os.path.join(ROOT, "marketing", "MarkupScreenshots")
OUT = os.path.join(ROOT, "marketing", "app-store", "screenshots", "ios-6.9")
os.makedirs(OUT, exist_ok=True)

# App Store "6.9-inch display" canvas (iPhone 16 Pro Max class). Apple accepts
# this size for every iPhone slot.
W, H = 1320, 2868

# Brand palette: navy → indigo gradient, light text, blue wordmark/accent.
GRAD_TOP = (13, 19, 38) # #0D1326 deep navy
GRAD_BOTTOM = (37, 26, 74) # #251A4A indigo
WORDMARK = (110, 168, 255) # #6EA8FF
ROOT = os.path.dirname(os.path.dirname(HERE))
SHOTS_DIR = os.path.join(ROOT, "marketing")
OUT_ROOT = os.path.join(ROOT, "marketing", "app-store", "screenshots")

# Brand palette: navy → indigo gradient, light text, blue wordmark.
GRAD_TOP = (13, 19, 38)
GRAD_BOTTOM = (37, 26, 74)
WORDMARK = (110, 168, 255)
HEADLINE = (255, 255, 255)
SUBLINE = (183, 193, 214) # #B7C1D6

CROP_TOP = 132 # drop the iOS status bar / "◀ TestFlight" band
CARD_W = 1040
CARD_TOP = 612
CARD_RADIUS = 46

# (source, headline (\n = line break), subline, output slug). Strongest first —
# the App Store shows the first 2–3 in search results.
SHOTS = [
("IMG_4042.PNG", "Read Markdown,\nbeautifully rendered",
"Code, math, diagrams, tables — in tabs", "01_read"),
("IMG_4041.PNG", "Open any GitHub repo\nas a vault",
"Read the whole repo — offline", "02_github-vault"),
("IMG_4039.PNG", "Bring your own folder\nor GitHub repo",
"iCloud, Files, or GitHub — nothing to import", "03_bring-your-own"),
("IMG_4038.PNG", "A reader-first\nMarkdown app",
"Light, dark & sepia themes", "04_reader-first"),
("IMG_4040.PNG", "Works with Files\n& iCloud Drive",
"Private by default — your notes stay yours", "05_files-icloud"),
]
SUBLINE = (183, 193, 214)


def load_font(size, bold=False):
"""Prefer SF Pro (the iOS system font); fall back to Helvetica/Arial."""
sf = "/System/Library/Fonts/SFNS.ttf"
if os.path.exists(sf):
try:
Expand All @@ -75,13 +50,13 @@ def load_font(size, bold=False):
return ImageFont.load_default()


def gradient(w, h, top, bottom):
img = Image.new("RGB", (w, h), top)
def gradient(w, h):
img = Image.new("RGB", (w, h), GRAD_TOP)
d = ImageDraw.Draw(img)
for y in range(h):
t = y / (h - 1)
d.line([(0, y), (w, y)], fill=tuple(
int(top[i] + (bottom[i] - top[i]) * t) for i in range(3)))
int(GRAD_TOP[i] + (GRAD_BOTTOM[i] - GRAD_TOP[i]) * t) for i in range(3)))
return img


Expand All @@ -94,54 +69,108 @@ def rounded(img, radius):
return img


def centered(draw, text, font, y, fill):
def centered(draw, text, font, y, fill, W):
w = draw.textlength(text, font=font)
draw.text(((W - w) / 2, y), text, font=font, fill=fill)
asc, desc = font.getmetrics()
return y + asc + desc


def compose(src_name, headline, subline, slug):
canvas = gradient(W, H, GRAD_TOP, GRAD_BOTTOM).convert("RGBA")
def compose(src_path, headline, subline, out_path, W, H,
wm_size, hl_size, sub_size, side, crop=None):
canvas = gradient(W, H).convert("RGBA")
draw = ImageDraw.Draw(canvas)

wm = load_font(46, bold=True)
centered(draw, "Markup", wm, 84, WORDMARK)

hf = load_font(98, bold=True)
y = 196
top_pad = round(H * 0.045)
y = centered(draw, "Markup", load_font(wm_size, True), top_pad, WORDMARK, W) + round(H * 0.018)
hf = load_font(hl_size, True)
for line in headline.split("\n"):
y = centered(draw, line, hf, y, HEADLINE) + 6
sf = load_font(46, bold=False)
y += 14
y = centered(draw, line, hf, y, HEADLINE, W) + 4
y += round(H * 0.012)
sf = load_font(sub_size, False)
for line in subline.split("\n"):
y = centered(draw, line, sf, y, SUBLINE) + 4

shot = Image.open(os.path.join(SRC, src_name)).convert("RGB")
shot = shot.crop((0, CROP_TOP, shot.width, shot.height))
card_h = round(CARD_W * shot.height / shot.width)
shot = shot.resize((CARD_W, card_h), Image.LANCZOS)
card = rounded(shot, CARD_RADIUS)
x = (W - CARD_W) // 2
yc = CARD_TOP
y = centered(draw, line, sf, y, SUBLINE, W) + 4

shot = Image.open(src_path).convert("RGB")
if crop:
shot = shot.crop(crop)
box_top = y + round(H * 0.03)
box_h = H - box_top - round(H * 0.05)
box_w = W - 2 * side
scale = min(box_w / shot.width, box_h / shot.height)
cw, ch = round(shot.width * scale), round(shot.height * scale)
shot = shot.resize((cw, ch), Image.LANCZOS)
radius = max(20, round(min(cw, ch) * 0.022))
card = rounded(shot, radius)
x = (W - cw) // 2
yc = box_top + (box_h - ch) // 2

shadow = Image.new("RGBA", (W, H), (0, 0, 0, 0))
ImageDraw.Draw(shadow).rounded_rectangle(
[x, yc + 30, x + CARD_W, yc + card_h + 30], radius=CARD_RADIUS,
fill=(0, 0, 0, 150))
[x, yc + round(H * 0.012), x + cw, yc + ch + round(H * 0.012)],
radius=radius, fill=(0, 0, 0, 150))
shadow = shadow.filter(ImageFilter.GaussianBlur(40))
canvas = Image.alpha_composite(canvas, shadow)
canvas.alpha_composite(card, (x, yc))
# Hairline rim so dark-mode screenshots separate from the dark gradient.
ImageDraw.Draw(canvas).rounded_rectangle(
[x, yc, x + CARD_W - 1, yc + card_h - 1], radius=CARD_RADIUS,
outline=(255, 255, 255, 48), width=2)
[x, yc, x + cw - 1, yc + ch - 1], radius=radius, outline=(255, 255, 255, 48), width=2)

os.makedirs(os.path.dirname(out_path), exist_ok=True)
canvas.convert("RGB").save(out_path, "PNG")
return out_path


# ---- iPhone 6.9" (1320×2868, portrait) ----
IPHONE = dict(folder="MarkupScreenshots-iphone", out="ios-6.9", W=1320, H=2868,
wm=46, hl=98, sub=46, side=140, crop=(0, 132, 1290, 2796))
IPHONE_SHOTS = [
("IMG_4042.PNG", "Read Markdown,\nbeautifully rendered", "Code, math, diagrams, tables — in tabs", "01_read"),
("IMG_4041.PNG", "Open any GitHub repo\nas a vault", "Read the whole repo — offline", "02_github-vault"),
("IMG_4039.PNG", "Bring your own folder\nor GitHub repo", "iCloud, Files, or GitHub — nothing to import", "03_bring-your-own"),
("IMG_4038.PNG", "A reader-first\nMarkdown app", "Light, dark & sepia themes", "04_reader-first"),
("IMG_4040.PNG", "Works with Files\n& iCloud Drive", "Private by default — your notes stay yours", "05_files-icloud"),
]

# ---- iPad 13" landscape (2752×2064) ----
IPAD = dict(folder="MarkupScreenshots-ipad", out="ipad-13", W=2752, H=2064,
wm=54, hl=112, sub=54, side=190, crop=(0, 52, 2388, 1668))
IPAD_SHOTS = [
("IMG_0203.PNG", "Read your vault on iPad", "Sidebar and reader, side by side", "01_split-read"),
("IMG_0202.PNG", "Open any GitHub repo as a vault", "Sign in, pick a repo, one tap to mount", "02_github-vault"),
("IMG_0204.PNG", "Light, dark & sepia", "Your reading, your way", "03_themes"),
("IMG_0205.PNG", "Pull the latest from GitHub", "Refresh your repo vault in place", "04_refresh"),
("IMG_0200.PNG", "A reader-first Markdown app", "Code, math, diagrams, tables", "05_reader-first"),
]

# ---- Mac (2880×1800, landscape) ----
MAC = dict(folder="MarkupScreenshots-mac", out="mac", W=2880, H=1800,
wm=54, hl=110, sub=52, side=170, crop=None)
MAC_SHOTS = [
("screenshot-20260618-205806.png", "Your Markdown,\nbeautifully rendered", "Sidebar, reader, and a live outline", "01_reader", None),
("screenshot-20260618-210215.png", "Plain Markdown, always", "No proprietary format — it's just your .md", "02_markdown", None),
("screenshot-20260618-210532.png", "Jump to any file", "Fuzzy quick-open across your vault", "03_quick-open", None),
("screenshot-20260618-205855.png", "A command for everything", "Tabs, sections, canvases — from the keyboard", "04_commands", None),
# 210149 has a Chrome window behind the app — crop to the app window (right portion).
("screenshot-20260618-210149.png", "Navigate big docs with ease", "Outline, search, tags & backlinks", "05_navigate", (1990, 150, 3456, 1994)),
]


out = os.path.join(OUT, f"{slug}.png")
canvas.convert("RGB").save(out, "PNG")
return out
def run(cfg, shots, per_shot_crop=False):
for s in shots:
if per_shot_crop:
src, hl, sub, slug, crop = s
crop = crop if crop is not None else cfg["crop"]
else:
src, hl, sub, slug = s
crop = cfg["crop"]
out = compose(
os.path.join(SHOTS_DIR, cfg["folder"], src), hl, sub,
os.path.join(OUT_ROOT, cfg["out"], f"{slug}.png"),
cfg["W"], cfg["H"], cfg["wm"], cfg["hl"], cfg["sub"], cfg["side"], crop)
print("wrote", os.path.relpath(out, ROOT))


if __name__ == "__main__":
for s in SHOTS:
print("wrote", compose(*s))
run(IPHONE, IPHONE_SHOTS)
run(IPAD, IPAD_SHOTS)
run(MAC, MAC_SHOTS, per_shot_crop=True)
Loading