# message-ui (Sendblue attachment cards)

Local setup of [pontusab/message-ui](https://github.com/pontusab/message-ui) — build rich
iMessage/WhatsApp attachment cards as React templates and export them to PNG, for sending
as Sendblue media attachments.

## Packages
- `@message-ui/render` — `renderToPng`, `renderToSvg` (Satori + resvg under the hood)
- `@message-ui/components` — Attachment, Heading, Text, Row, Column, Section, List, Divider,
  Spacer, Avatar, Image, DonutChart, LineChart, ActivityRings
- `@message-ui/tailwind`
- peer: React 18/19 (installed)

Note: docs reference a `npx message-ui dev` CLI, but no `message-ui` CLI package is published
yet (v0.1.0). The working path is the programmatic `renderToPng` API.

## Layout
- `attachments/` — one template per card (`.mjs`, default-export a component)
- `render.mjs` — renders a template to `out/<name>.png`
- `out/` — exported PNGs

## Usage
```bash
node render.mjs           # writes out/example-summary.png
```
Templates use `React.createElement` (no JSX build step). Pass data via props.

## Sending via Sendblue
`renderToPng` returns PNG bytes → write to disk. Sendblue's `reply` tool takes a
`media_url`, which must be a PUBLICLY REACHABLE URL (not a local path). So the send
pipeline is: render PNG -> host at a public URL -> reply(chat_id, text, media_url).
The hosting step (e.g. via the existing Sendblue server / tunnel, or an upload endpoint)
is not wired yet — see Gautam before adding outbound hosting.

## Sending: `send.mjs`
```bash
MUI_PUBLIC_BASE="https://<public-base>/media" \
  node send.mjs --to +1XXXXXXXXXX --template ./attachments/example-summary.mjs \
  --text "Daily summary" --props '{"title":"Exult — Daily Summary"}'
```
Reads Sendblue creds from env (`SENDBLUE_API_KEY_ID`, `SENDBLUE_API_SECRET_KEY`,
`SENDBLUE_OWN_NUMBER`) — never commit these. Renders the template, writes
`out/<name>.png`, and POSTs `media_url = $MUI_PUBLIC_BASE/<name>.png` to Sendblue.

### Hosting options (Sendblue fetches media_url server-side)
1. **Durable (recommended):** serve this package's `out/` dir over the existing
   Tailscale funnel by adding a read-only `GET /media/<file>` route to the
   sendblue-channel server. Then `MUI_PUBLIC_BASE=https://<funnel-host>/media`.
2. **Zero-infra (used for the first example):** `python3 -m http.server 18820
   --bind 127.0.0.1 --directory out` + `cloudflared tunnel --url
   http://127.0.0.1:18820`; use the printed `trycloudflare.com` URL as the base.
   Ephemeral — fine for one-off sends.

### ⚠️ PHI caution
Cards rendered from clinic data (patient names, encounters, etc.) become a public
image at `media_url`. Do **not** host PHI at a public URL. Keep cards to
non-identifying aggregates, or use a hosting option with auth/expiry before
sending anything with PHI.
