# How OfficeCLI Watch Command Provides Live Browser Preview and Auto-Refresh

> Discover how the OfficeCLI watch command delivers live browser preview and auto-refresh for Office documents. It renders documents as HTML and instantly updates your browser on changes.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: how-to-guide
- Published: 2026-08-05

---

**The `officecli watch` command launches a local Server-Sent Events (SSE) server that renders Office documents as HTML and automatically refreshes the browser whenever the document changes, using named-pipe IPC to receive real-time updates from other CLI commands.**

This article explains the complete architecture behind OfficeCLI's live preview system, from initial HTML generation to incremental DOM updates in the browser.

## Overview of the Watch Architecture

The live preview system consists of two core components working in tandem: the **WatchServer** ([`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs)) that hosts the SSE endpoint and maintains the HTML snapshot, and the **WatchNotifier** ([`WatchNotifier.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchNotifier.cs)) that other commands use to broadcast changes.

The design keeps document parsing separate from the server process. The watch command never holds the Office file open—instead, it works with an in-memory HTML representation, avoiding file-lock conflicts that would block editing tools.

## Starting the Watch Server

When you run `officecli watch mydoc.docx`, the command executes three initialization steps defined in [`CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Watch.cs) (lines 34–70):

1. **Attempts daemon communication** — calls `ResidentClient.TrySend` to request HTML from a running `officecli` resident process
2. **Falls back to direct rendering** — if no daemon responds, opens the document via the appropriate handler (`PowerPointHandler`, `ExcelHandler`, or `WordHandler`) and calls `RenderViaRegistry`
3. **Launches WatchServer** — starts the TCP listener on `http://localhost:26315` (default port, configurable via environment)

```bash

# Start watching a PowerPoint presentation

officecli watch presentation.pptx

# Custom port and idle timeout

OFFICECLI_WATCH_PORT=8080 OFFICECLI_WATCH_IDLE_SECONDS=300 officecli watch report.docx

```

The server prints its URL to stdout: `Watch: http://localhost:26315`. Opening this URL loads the current HTML snapshot plus two embedded JavaScript resources: [`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js) and [`watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-overlay.js).

## Server-Sent Events: The Update Mechanism

[`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs) (lines 81–91) implements a pure **Server-Sent Events (SSE)** relay. Unlike WebSockets, SSE operates over standard HTTP with unidirectional server-to-client streaming—ideal for one-way refresh notifications.

The server injects an EventSource script block into every served page:

```html
<!-- Simplified representation of injected code -->
<script>
const source = new EventSource('/events');
source.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  // apply full refresh, slide patch, or scroll command
};
</script>

```

This establishes a persistent connection where the server can push updates at any time.

## How Commands Signal Changes

Every `officecli` command that mutates a document—`set`, `add`, `remove`, `mark`—triggers a refresh notification through [`WatchNotifier.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchNotifier.cs) (lines 15–25). The notifier:

1. Computes a hash of the watched file path to locate the correct named pipe
2. Opens `officecli-watch-<hash>` (platform-specific named pipe / Unix domain socket)
3. Sends a JSON message with update details

The message schema supports two update modes:

| Field | Type | Purpose |
|-------|------|---------|
| `FullHtml` | string | Complete replacement HTML snapshot |
| `action` + `slide` + `html` | object | Incremental patch for specific slide/section |

```csharp
// From WatchNotifier.cs - simplified message structure
{
  "FullHtml": "<html>...</html>",           // or
  "action": "replace",
  "slide": 3,
  "html": "<div class='slide'>...</div>",
  "scroll": "#slide-3 .title"
}

```

Named pipes provide cross-platform IPC without requiring TCP ports or HTTP overhead. Windows uses named pipes; Linux and macOS use Unix domain sockets in the temp directory.

## Processing Updates on the Server

[`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs) runs `RunPipeListenerAsync` continuously. When `HandleSinglePipeClientAsync` receives a message, it delegates to `HandleWatchMessage` (lines 118–143) which:

- **For full refreshes**: Replaces `_currentHtml` entirely
- **For patches**: Applies mutations via `PatchSlideInHtml`, `AppendSlideToHtml`, or `RemoveSlideFromHtml`

After updating the cached snapshot, the server:

1. Increments an internal version counter
2. Calls `SendSseEvent` to broadcast to all connected browsers
3. Includes the `scroll` selector if provided, enabling automatic navigation to changed content

```bash

# Terminal 1: start watch

officecli watch proposal.docx

# → Watch: http://localhost:26315

# Terminal 2: modify document - update triggers automatically

officecli set proposal.docx --paragraph 2 --text "Revised pricing structure"

# Terminal 3: add visual annotation

officecli watch mark proposal.docx "/body/table[1]" --color "#ff6600" --note "Review this table"

```

## Client-Side DOM Manipulation

The embedded [`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js) handles three update types:

- **`full`**: Replaces `document.body.innerHTML` with the complete new snapshot
- **`replace`/`add`/`remove`**: Patches specific slide elements for PowerPoint, or computes `ComputeWordPatches` for Word documents to apply block-level diffs
- **`scroll`**: Executes `document.querySelector(msg.scroll).scrollIntoView()` when a selector is provided

The [`watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-overlay.js) layer adds UI features: visual mark indicators, selection highlighting, and rubber-band selection tools. These operate independently of the core refresh logic.

## Idle Shutdown and Lifecycle Management

[`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs) (lines 55–73) implements an idle watchdog to conserve resources. Configuration via environment variables:

| Variable | Default | Behavior |
|----------|---------|----------|
| `OFFICECLI_WATCH_IDLE_SECONDS` | 300 (5 min) | Terminates server if no SSE clients connected |
| `OFFICECLI_WATCH_PORT` | 26315 | TCP port for HTTP/SSE server |

Graceful shutdown handles SIGTERM, SIGHUP, SIGQUIT, and Ctrl+C through `StopAsync`, which:

- Cancels the pipe listener
- Closes all TCP connections
- Deletes the on-disk marker file (`officecli-watch-<hash>.pid`)
- Exits cleanly without orphaning resources

## Mark Commands and Real-Time Annotations

The watch system supports persistent annotations through sub-commands:

```bash
officecli watch mark document.docx "/body/p[3]" --color "#00aa00" --note "Key finding"
officecli watch unmark document.docx "/body/p[3]"
officecli watch marks document.docx  # list all marks

```

Mark metadata is stored in-memory by `WatchServer` and pushed to browsers via the same SSE channel. The overlay script renders colored highlights and tooltips based on this data.

## Key Source Files

| Path | Responsibility |
|------|---------------|
| [[`src/officecli/CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs) | CLI argument parsing, initial HTML acquisition, server spawning |
| [[`src/officecli/Core/Watch/WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs) | SSE server, named-pipe listener, HTML cache, idle watchdog |
| [[`src/officecli/Core/Watch/WatchNotifier.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchNotifier.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchNotifier.cs) | IPC client for broadcasting updates from other commands |
| [`Resources/watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Resources/watch-sse-core.js) (embedded) | Browser-side SSE connection, DOM patching, scroll handling |
| [`Resources/watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Resources/watch-overlay.js) (embedded) | UI decorations, mark visualization, selection tools |
| [`src/officecli/Core/ResidentClient.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/ResidentClient.cs) / [`ResidentServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/ResidentServer.cs) | Optional daemon mode for lock-free initial renders |

## Summary

- **OfficeCLI watch** creates a self-contained SSE server on `localhost:26315` that serves HTML snapshots of Office documents
- **Named-pipe IPC** (`officecli-watch-<hash>`) enables any CLI command to push refresh notifications without file-lock conflicts
- **Incremental updates** support both full HTML replacement and targeted patches for slides or document sections
- **Client-side JavaScript** applies DOM changes in real-time and handles automatic scrolling to modified content
- **Resource management** includes idle timeout (default 5 minutes), graceful signal handling, and clean shutdown

## Frequently Asked Questions

### What port does OfficeCLI watch use by default?

The default port is **26315**. You can override it with the `OFFICECLI_WATCH_PORT` environment variable. The server binds to `localhost` only—there is no built-in option to listen on external interfaces, as the preview is intended for local development use.

### Why does the watch command use Server-Sent Events instead of WebSockets?

SSE was chosen because the communication pattern is strictly **server-to-client** (the browser never needs to send data upstream). SSE operates over standard HTTP, handles reconnection automatically, and requires no special protocol negotiation. WebSockets would add complexity without benefit for this unidirectional notification pattern.

### Can multiple browsers connect to the same watch server simultaneously?

Yes. `WatchServer` maintains a collection of active SSE connections and broadcasts each update to all connected clients through `SendSseEvent`. However, `marks` and other stateful interactions are synchronized per-server, not per-client—annotations appear identically across all browser windows.

### What happens if I edit the document with a GUI application while watching?

The watch server detects changes only through `officecli` commands that use `WatchNotifier`. External edits do not trigger automatic refresh. To update the preview after external changes, run any `officecli` command that touches the file (such as `officecli get document.docx --version`) or restart the watch server. The resident daemon mode (`ResidentClient`) can reduce this limitation by providing a polling-based fallback in future versions.