# @vojo/widget-telegram

Vojo Telegram bridge management widget — mounts inside `/bots/telegram`
in the Vojo client. See [`docs/plans/bots_tab.md`](../../docs/plans/bots_tab.md)
Phase 3 for product context and the matrix-widget-api contract.

This is **not** a Telegram client. It's a small panel that drives the
mautrix-telegram bridge bot (`@telegrambot:vojo.chat`) by sending text
commands in the control DM and rendering the bot's text replies. M11
ships only the bootstrap + a `ping` button to verify the host handshake.

## Layout

```
src/
├── bootstrap.ts    Parse URL params the host appends (matches BotWidgetEmbed.ts)
├── widget-api.ts   Inline matrix-widget-api postMessage transport (no SDK)
├── App.tsx         UI: bootstrap card, action buttons, transcript pane
├── main.tsx        Entry: init bootstrap, render App or diagnostic
└── styles.css      Theme-aware CSS variables
```

## Local development

**Don't touch the committed `config.json`.** Create `config.local.json` at
the project root once — gitignored, never deployed. The host's Vite dev
server overlays it on top of `/config.json` responses (see
`serveLocalConfigOverlay` in `vite.config.js`); prod builds ignore the
overlay entirely.

```bash
# one-time: install widget deps
cd apps/widget-telegram && npm install

# one-time: create config.local.json (gitignored) at the project root
cat > /home/ubuntu/projects/vojo/cinny/config.local.json <<'JSON'
{
  "bots": [
    {
      "id": "telegram",
      "experience": {
        "type": "matrix-widget",
        "url": "http://localhost:8081/"
      }
    }
  ]
}
JSON
```

The overlay merges `bots[]` by `id`, so just `{ id, experience }` is
enough — base bot's `mxid` and `name` are preserved. Top-level fields
not present in `config.local.json` are inherited from `config.json`.

Run both servers:

```bash
# terminal 1 — widget on :8081 with HMR
cd apps/widget-telegram && npm run dev

# terminal 2 — host SPA on :8080
cd /home/ubuntu/projects/vojo/cinny && npm start
```

Open `http://localhost:8080/bots/telegram`. Iframe loads cross-origin
from the widget dev server, HMR works, no proxy.

`http://localhost:*` URLs are accepted by the host's URL validator only
in dev builds (`import.meta.env.DEV` branch in
`src/app/features/bots/catalog.ts`); production builds drop the branch
via Vite's dead-code elimination, AND production-only enforces an origin
allowlist (`PROD_WIDGET_ORIGINS`) so prod can never embed `localhost` even
if config.json is poisoned.

Deploy is unchanged. `config.local.json` is gitignored, never shipped.
You don't need to revert anything before `Deploy to vojo.chat` — there
is nothing in tracked files that points at localhost.

Standalone preview of the widget bundle (no host, useful for visual
iteration):

```bash
cd apps/widget-telegram
npm run dev      # vite dev server on :8081 — shows missing-params banner
                 # without host, expected.
npm run preview  # serves the production build from dist/
```

## Build

```bash
npm run build
```

Outputs to `apps/widget-telegram/dist/`. Deploy by rsyncing `dist/*`
into `~/vojo/widgets/telegram/` on the production host (Caddy serves
this via the `widgets.vojo.chat` block). One parent `~/vojo/widgets/`
directory hosts every bot widget — adding a second one is `mkdir
~/vojo/widgets/<slug>/` plus a Caddy block, no docker-compose edit.

## Hosting (server-side, runbook)

1. DNS: `widgets.vojo.chat` A/AAAA → server. Verify with `dig`.
2. `~/vojo/docker-compose.yml` — Caddy `volumes:` adds (one parent mount,
   future widgets reuse it):
   ```yaml
   - ./widgets:/var/www/widgets
   ```
3. `~/vojo/caddy/Caddyfile` — append:
   ```
   widgets.vojo.chat {
       encode zstd gzip

       header {
           Content-Security-Policy "frame-ancestors https://vojo.chat https://localhost"
           X-Content-Type-Options "nosniff"
           Referrer-Policy "no-referrer"
           Cache-Control "no-cache, no-store, must-revalidate"
           -Server
       }

       handle_path /telegram/* {
           root * /var/www/widgets/telegram
           try_files {path} /index.html
           file_server
       }

       handle {
           respond "Not Found" 404
       }
   }
   ```
4. `mkdir -p ~/vojo/widgets/telegram` (placeholder so cert provisioning
   has something to serve), then `docker compose up -d caddy` to apply.
5. Verify directly: `curl -I https://widgets.vojo.chat/telegram/index.html`
   should return 200 and the `Content-Security-Policy` header.

## Updating the production /config.json

Once the widget is live at `https://widgets.vojo.chat/telegram/index.html`,
add to the host repo's `config.json`:

```json
"experience": {
  "type": "matrix-widget",
  "url": "https://widgets.vojo.chat/telegram/index.html"
}
```

## Capacitor (Android)

`capacitor.config.ts` already has a placeholder. Uncomment and set:

```ts
server: { allowNavigation: ['widgets.vojo.chat'] }
```

Without this, Android's WebView hijacks the cross-origin iframe URL into
`Intent.ACTION_VIEW` and the iframe stays blank. Rebuild the APK after.

## Capability contract

The widget requests EXACTLY this set (matches the host's
`BotWidgetDriver.getBotWidgetCapabilities`):

```
org.matrix.msc2762.timeline:<roomId>
org.matrix.msc2762.send.event:m.room.message#m.text
org.matrix.msc2762.receive.event:m.room.message#m.text
org.matrix.msc2762.receive.event:m.room.message#m.notice
org.matrix.msc2762.receive.state_event:m.room.member
```

Anything else is silently dropped by the host. To extend the surface,
update `BotWidgetDriver.ts` upstream — that requires a security review
per Phase 2 plan §M9.
