# Netwatch Widget

An AI-powered monitoring assistant shipped as a self-contained Web Component
(Shadow DOM — styles are fully isolated from the host page). Drop in one script
tag to add a chat bubble to any page.

Published to npm as **`@netwatchai/chat`** and served over jsDelivr:

```
https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js
```

(Pin a version with `@0.1.0` instead of `@latest` for stability.)

## Features

- 💬 Real-time streaming chat interface
- 📱 Fully responsive and embeddable
- 🎨 Tailwind CSS styling with complete style isolation
- 🔄 Web Component with Shadow DOM
- ↔️ Resizable and expandable interface
- 🌙 Dark mode support
- 📦 Distributable via CDN

## Embedding

Two equivalent ways — both load the single bundle from jsDelivr.

### 1. Declarative (custom element)

```html
<script type="module"
  src="https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js"></script>

<netwatch-widget
  client-id="b48ea317-…"
  api-endpoint="http://your-api:8000">
</netwatch-widget>
```

### 2. Programmatic (one-liner — PHP / CMS friendly)

```html
<script type="module">
  import NetwatchChat from "https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js";
  NetwatchChat.initBubble({
    clientId: "b48ea317-…",
    apiHost:  "http://your-api:8000",
  });
</script>
```

### Options

| Attribute      | `initBubble` key      | Required | Notes |
|----------------|-----------------------|----------|-------|
| `client-id`    | `clientId`            | yes      | Netwatch client UUID |
| `api-endpoint` | `apiHost` / `apiEndpoint` | yes  | API base URL (a trailing `/api/chat` is stripped automatically) |
| `username`     | `username`            | no       | **Auto-resolved** from the host page's localStorage when embedded in the Netwatch UI. Pass explicitly to override, or when embedding on other hosts. |

## Embedding in the Netwatch monitoring UI

The widget auto-reads the logged-in user from the host page's localStorage, so
it only needs its script injected on every (logged-in) page. The UI has no
built-in custom-JS setting, so use one of:

**Option A — reverse proxy injection (recommended, survives upgrades).** With
nginx in front of the frontend:

```nginx
location / {
    proxy_pass http://frontend-upstream;
    proxy_set_header Accept-Encoding "";   # sub_filter needs uncompressed HTML
    sub_filter_once on;
    sub_filter_types text/html;
    sub_filter '</body>'
      '<script type="module">import N from "https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js";N.initBubble({clientId:"b48ea317-…",apiHost:"http://your-api:8000"});</script></body>';
}
```

**Option B — edit the frontend's page layout template** (`ui/include/views/layout.htmlpage.php`),
just before the `</body>` output. ⚠️ This is overwritten on every upgrade, so
reapply it after upgrading:

```php
echo '<script type="module">import N from "https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js";N.initBubble({clientId:"b48ea317-…",apiHost:"http://your-api:8000"});</script>';
```

> ⚠️ **Mixed content:** if the UI is served over HTTPS but `api-endpoint` is
> `http://…`, browsers silently block the API calls. Serve the API over HTTPS
> too, or keep both on plain HTTP within a private network/tunnel.

## Development

```bash
pnpm install

# Copy the env template and fill it in.
cp .env.example .env
```

Edit `.env`:

```
VITE_API_ENDPOINT=http://localhost:8000
VITE_DEV_CLIENT_ID=your-client-uuid
VITE_DEV_USERNAME=your-netwatch-username
```

Both `.env` and `.env.local` are gitignored; only `.env.example` is committed.

Start the dev server:

```bash
pnpm run dev
```

Visit `http://localhost:5173` for the test page; it reads the env vars via
`src/config.js` and mounts the widget automatically.

### Build

```bash
pnpm run build
```

Outputs to `lib/` (the `@latest/lib/web.min.js` jsDelivr path):

- `lib/web.min.js` — ES module (modern `<script type="module">` / npm import)
- `lib/web.umd.js` — UMD build (legacy `<script>` tag → `window.NetwatchChat`)

## Publishing / Deploying

Published to npm as `@netwatchai/chat` by `.github/workflows/widget-publish.yml`,
**triggered by pushing a `widget-v*` git tag**. The version is taken from the
tag, so a release never commits back to the protected `main` branch and needs
no privileged token — just `NPM_TOKEN`. Consumers pick it up via jsDelivr at
`https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js`.

> **Quick path:** bump the version (changesets, below) and commit, then run
> `pnpm run release:tag` from `widget/` — it validates, builds, tags, pushes,
> and watches the publish run. Full reference: [docs/widget-release.md](../docs/widget-release.md).

### Release steps

1. **Bump the version on `main` via a normal PR.** Easiest is to consume any
   changesets into `package.json` + `CHANGELOG.md`:

   ```bash
   cd frontend/widget
   pnpm changeset             # (optional) describe the change → feeds CHANGELOG
   pnpm run version-packages  # bumps package.json + CHANGELOG, consumes changesets
   ```

   Keep changeset summaries in Netwatch terms — they become the public
   `CHANGELOG.md`. Commit, open a PR, and merge it to `main`.

2. **Tag the merge commit and push the tag** (must match `package.json`):

   ```bash
   git fetch origin
   git tag widget-v0.6.0 origin/main      # widget-v<version>
   git push origin widget-v0.6.0
   ```

3. The workflow fires on the tag: it sets `package.json` to the tag's version,
   builds, and runs `npm publish`. It **fails loudly** if that version is
   already on npm (bump and re-tag) or if the tag isn't `widget-v<semver>`.

> **Why tags, not push-to-main?** The org ruleset requires PRs on `main` and
> blocks the Actions bot from pushing — so a CI-driven version-bump commit
> can't land. Pushing a tag sidesteps the protected branch entirely. (The
> earlier auto-on-merge workflow failed silently for exactly this reason.)

Requires repo secret **`NPM_TOKEN`** — a *granular* npm token with **"Bypass
two-factor authentication (2FA)" checked** and read/write on the `@netwatchai`
scope (a token without the 2FA bypass fails publish with `EOTP`).

### What a release looks like (the `0.7.0` example)

The exact sequence used to cut `@netwatchai/chat@0.7.0` (pnpm commands from
`widget/`, git from anywhere in the repo):

```bash
# 1. Describe the change (feeds CHANGELOG); pick patch / minor / major.
pnpm changeset                         # or hand-write .changeset/<slug>.md

# 2. Roll pending changesets into the version + CHANGELOG.
pnpm run version-packages              # e.g. 0.6.0 → 0.7.0, deletes the changeset

# 3. Commit the bump and push the branch.
git commit -am "chore(widget): release 0.7.0 (SLA settings UI)"
git push origin <branch>

# 4. Tag that commit and push the tag — pushing the tag is what publishes.
git tag widget-v0.7.0
git push origin widget-v0.7.0          # → fires .github/workflows/widget-publish.yml
```

The tag may point at **any commit**, not just `main`: CI checks out the tagged
commit, sets `package.json` to the tag's version, builds, and publishes. `0.7.0`
was cut from a feature branch this way — shipping the widget ahead of merging its
backend PR. Prefer tagging `main` for normal releases.

### Verify the publish

```bash
gh run watch --exit-status          # follow the "Widget publish" run to green
npm view @netwatchai/chat version   # → 0.7.0
```

Live within a few minutes via jsDelivr at
`https://cdn.jsdelivr.net/npm/@netwatchai/chat@latest/lib/web.min.js`
(`@latest` resolves to the new version; pin `@0.7.0` to be explicit).

## Architecture

- **Web Components** — native browser API for encapsulation
- **Shadow DOM** — complete style isolation from the parent page
- **React** — internal rendering framework
- **Vercel AI SDK** — streaming chat interface
- **Tailwind CSS** — utility-first styling
- **Streamdown** — markdown rendering

## License

MIT
