eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– Marketing screenshots β€” native Mac + iPhone captures for the hero collage
2
3How we capture the native **Mac** and **iPhone** screenshots that sit in the
4marketing site's three-device hero collage (alongside a web frame). This is the
5committed, hand-operable form of a capture recipe that previously lived only in
6a throwaway branch and agent memory β€” the "outlive-AI" rule in action: anyone,
7human or agent, can stand the demo up and reproduce the shots from this doc plus
8`tools/marketing-shots/`, with no special knowledge encoded elsewhere.
9
10The whole flow is fictional and local. Nothing here ever points an app at a
11real or production instance, and the Mac app is built in an **isolated identity**
12so it can only ever show the seeded demo space β€” never anyone's real signed-in
13spaces (see *Isolation* below β€” it is the load-bearing safety property).
14
15The tool stands up the demo and points you here; the interactive captures
16themselves stay **manual** on purpose (a person frames and takes each shot).
17
18## Quick start (`./tools/dev shots`)
19
20```sh
21./tools/dev shots up # Terminal 1 β€” build the isolated demo Mac app (if
22 # needed) + run a throwaway local server seeded with
23 # the fictional "The Commons" space. Foreground:
24 # prints the local URL + a localhost sign-in link;
25 # Ctrl-C stops it.
26./tools/dev shots tunnel # Terminal 2 β€” give the local server a real public
27 # https try-*.11relay.com address (via `lchat try
28 # --local`) + single-use Mac & iPhone sign-in links.
29./tools/dev shots mac # Launch the isolated "Eleven Demo" Mac app.
30./tools/dev shots web-check shot.png # Headless 2x screenshot of the seeded
31 # space, to eyeball the look before capturing native.
32```
33
34Implementation lives in `tools/marketing-shots/` (`serve.sh`, `tunnel.sh`,
35`seed-team.mjs`, `demo-lib.mjs`, `device-link.mjs`, `web-check.mjs`) β€” the
36`shots` verbs are thin wrappers, mirroring how `./tools/dev up` wraps
37`tools/lchat-dev-up.mjs`. Runtime artifacts (the demo DB, state, PNGs) are
38gitignored.
39
40## The demo space: "The Commons"
41
42A fictional civic-tech team space that matches the web hero frame
43(`website/src/assets/hero/team-web.png`). It's seeded **through the real E2EE
44protocol** (`demo-lib.mjs` provisions each member via an admin key-invite β†’ claim
45β†’ pubkey, then encrypts every thread/message client-side exactly as the browser
46does) β€” the server only ever holds ciphertext, so there is no plaintext
47shortcut. Six threads: **The commons 🌱** (the flagship β€” the exact web-frame
48conversation: Aoife / Marcus / Devi, a reply, and reactions), Standup, Shipping
49πŸš€, Field notes πŸ““, Roadmap, Watercooler β˜•. You sign in as **Cass** (your own
50messages are the accent bubbles on the right, matching the web frame's reply).
51
52Eyeball it any time without a native build:
53
54```sh
55./tools/dev shots web-check /tmp/commons.png && open /tmp/commons.png
56```
57
58## Isolation β€” why this is safe (load-bearing)
59
60A normal Mac build reads the **real** account. Both `LChatMac.entitlements` and
61`LChatMac-Dev.entitlements` carry
62`keychain-access-groups = $(AppIdentifierPrefix)com.elevenmessenger.messenger.shared`,
63and a dev alt (`./tools/dev alt`) shares that same group **on purpose** β€” an alt
64is the real account, not a sandbox, and has grabbed Paul's private space before.
65That is exactly what we must NOT do for a marketing screenshot.
66
67So the demo Mac build is signed with **`ios/LChatMac/LChatMac-Demo.entitlements`**
68β€” minimal by design (only `app-sandbox`, `network.client`, and
69`files.user-selected.read-write`), with the shared keychain group (and the app
70group, push, and Sparkle exceptions) **removed**. No shared keychain group β†’
71the build has its **own empty keychain** and can only show the demo space you
72add. It also gets a distinct bundle id, `com.elevenmessenger.messenger.mac.capdemo`
73(its own sandbox container + Launch Services identity, so it runs beside the real
74app), and the display name "Eleven Demo". The real purple `AppIcon` is kept on
75purpose so the screenshot shows the real brand mark.
76
77**Crucially, none of this touches `project.yml`.** `./tools/dev shots` applies
78the demo identity purely as xcodebuild **command-line build-setting overrides**
79(`CODE_SIGN_ENTITLEMENTS=<abs demo path>`,
80`PRODUCT_BUNDLE_IDENTIFIER=…mac.capdemo`, `LCHAT_MAC_DISPLAY_NAME=Eleven Demo`),
81which outrank the target/config values in the generated project. A plain
82`./tools/dev build mac` (or `xcodegen generate`) therefore still resolves the
83normal entitlements (Release β†’ `LChatMac.entitlements`, Debug β†’
84`LChatMac-Dev.entitlements`) and the real `com.elevenmessenger.messenger.mac`
85bundle id β€” the released app is never at risk.
86
87Verify the isolation on the built app any time β€” **no output means isolated**:
88
89```sh
90codesign -d --entitlements :- \
91 "ios/build-demo/Build/Products/Debug/Eleven Demo.app" | grep -i keychain
92# β†’ (no output) = no keychain-access-groups = isolated, own empty keychain
93```
94
95## Capturing the Mac shot
96
971. `./tools/dev shots up` (Terminal 1), then `./tools/dev shots tunnel`
98 (Terminal 2) for a real https origin the native apps can reach.
992. `./tools/dev shots mac` to launch "Eleven Demo". In the app: **Spaces β–Έ Add
100 from link…** and paste the **Mac** link `shots tunnel` printed (signs you in
101 as Cass). If a "Keep your access safe / Sign in" card appears, dismiss it with
102 "Remind me later".
1033. **Leave the Mac on its default Classic light theme** (light background, blue
104 accent bubbles) β€” that is what `hero-mac.png` uses and what the collage wants
105 for the natives; do **not** switch it to Lit Room (see *Theme pairing*). Open
106 **The commons 🌱** to mirror the web frame, or **Standup** for variety (it has
107 an own-bubble + a reaction).
1084. **Capture full-screen, then crop with PIL** β€” the robust path. Interactive
109 `screencapture -w` (click a window) and System Events window-bounds both rely
110 on Accessibility / Quartz (pyobjc), which isn't granted on the build Mac and
111 is unreliable when driven headlessly. Full-screen + crop needs none of that:
112
113 ```sh
114 screencapture -o ~/Desktop/team-mac-raw.png # -o = no window shadow; full screen, 2x retina
115 ```
116
117 Crop the app-window region to the collage aspect (`hero-mac.png` is
118 **1300Γ—919**, aspect β‰ˆ **1.415**) and scale to 1300Γ—919:
119
120 ```python
121 from PIL import Image
122 im = Image.open("team-mac-raw.png") # full-screen 2x capture
123 # Pick a crop box (left, top, right, bottom) around the window, keeping
124 # (right-left)/(bottom-top) β‰ˆ 1.415 so nothing is squashed on the resize.
125 box = (LEFT, TOP, RIGHT, BOTTOM)
126 im.crop(box).resize((1300, 919), Image.LANCZOS).save("team-mac.png")
127 ```
128
129## Capturing the iPhone shot β€” REAL DEVICE ONLY
130
131> ⚠️ **Do NOT use the iOS Simulator.** The iOS 26.3.1 simulator renders emoji as
132> tofu (β–‘) β€” confirmed 2026-07-28, both in-app and in Safari. Use a real iPhone.
133
1341. `./tools/dev shots tunnel` prints an **iPhone** link. Open it **on the
135 device** (paste into Safari β†’ Open in the app / join) β€” it signs the phone in
136 as Cass. The phone is the real app, so it'll show this demo space alongside
137 any real spaces; scroll to / open "The Commons" for the shot.
1382. Land on the space's **thread list** (the frame in `hero-ios.png`: title + the
139 six thread rows with previews).
1403. Screenshot on the device (**Volume-Up + Side button**) and AirDrop it to the
141 Mac. An iPhone 16 Pro shot is **1206Γ—2622** (aspect β‰ˆ 0.460).
142
143### Reusing the existing bezel frame
144
145The website's `hero-ios.png` (**760Γ—1554**, RGBA, transparent surround) already
146has the Apple bezel baked in. Rather than re-render a device frame, drop the new
147screenshot into that same bezel. Its screen rect is **(41, 39)** with size
148**678Γ—1476** (aspect β‰ˆ 0.459 β€” the raw 1206Γ—2622 shot's 0.460 is a near-perfect
149match, so scaling barely distorts), corners rounded at **r β‰ˆ 92px**. PIL only
150(numpy is **not** installed on the build Mac):
151
152```python
153from PIL import Image, ImageDraw
154
155BEZEL = "hero-ios.png" # 760Γ—1554, Apple bezel baked in, transparent surround
156RAW = "team-ios-raw.png" # on-device screenshot (e.g. 1206Γ—2622, iPhone 16 Pro)
157OUT = "team-ios.png"
158
159X, Y, W, H, R = 41, 39, 678, 1476, 92 # screen rect + corner radius in the bezel
160
161frame = Image.open(BEZEL).convert("RGBA")
162shot = Image.open(RAW).convert("RGBA").resize((W, H), Image.LANCZOS)
163
164# Rounded-rect mask so the shot's corners tuck under the bezel's screen cutout.
165mask = Image.new("L", (W, H), 0)
166ImageDraw.Draw(mask).rounded_rectangle([0, 0, W, H], radius=R, fill=255)
167
168frame.paste(shot, (X, Y), mask)
169frame.save(OUT)
170```
171
172You may **read** `website/src/assets/hero/hero-ios.png` as the reference bezel,
173but the composite output belongs in the website repo β€” do not modify the website
174repo from a messenger-repo change.
175
176## Theme pairing
177
178The collage deliberately pairs a **warm** web frame (Lit Room cream) with
179**lighter** natives (Classic light / blue). So only the **web** frame is Lit
180Room; **both natives stay on the default Classic light** (blue accent) β€” the
181Mac app's out-of-the-box theme, matching `hero-mac.png` and the iPhone. Don't
182switch the Mac to Lit Room, or all three frames read as the same cream wash.
183
184## Where the outputs go
185
186The finished frames live in the **website** repo (not this one):
187
188- `website/src/assets/hero/team-web.png`, `team-mac.png`, `team-ios.png` β€” wired
189 into the homepage `.collage` in `website/src/index.njk`.
190- The original `hero-web.png` / `hero-mac.png` / `hero-ios.png` family (a
191 household space) is used on `/families/`.
192
193Committing and deploying the website is a separate, website-repo concern (its
194own deploy path) β€” out of scope for this kit, which only produces the raw + framed
195images.
196
197## Known cosmetic gaps (not seed bugs)
198
199- **"6 members Β· N online":** the header's *online* count reflects who is really
200 connected (you), so it reads "6 members" or "6 members Β· 1 online", not the
201 web frame's mocked "3 online". Faking more would need extra live connections β€”
202 not worth it for a still.
203- **Thread-list times** all read the same recent minute (the server stamps
204 `created_at` at seed time). In-thread times match the web ("Today 9:12 AM")
205 because those come from the encrypted payload.
206
207## Teardown
208
209- Ctrl-C the tunnel (Terminal 2) to retire the public address, then Ctrl-C the
210 server (Terminal 1). The demo DB is wiped on the next `shots up` anyway.
211- Quit "Eleven Demo".
212- Optional: `rm -rf ios/build-demo` and the gitignored
213 `tools/marketing-shots/.demo*` runtime files when the collage is done.
214