# How OfficeCLI Watch Enables Live Browser Previews with Auto‑Refresh

> Learn how the OfficeCLI watch command enables live browser previews with automatic refresh. It efficiently relays real-time document updates to your browser as HTML.

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

---

**The `officecli watch` command starts a Server‑Sent‑Events (SSE) relay server that renders Office documents as HTML and pushes real‑time updates to connected browsers whenever the document changes.**

OfficeCLI's `watch` command transforms static Office files into live, auto‑refreshing web previews. This article explains the complete implementation—from the initial HTML snapshot acquisition through real‑time DOM synchronization—based on the actual source code in the iOfficeAI/OfficeCLI repository.

## Architecture Overview

The watch system consists of three coordinated components:

- **WatchServer** – a lightweight TCP listener that serves HTML and manages SSE connections
- **WatchNotifier** – a named‑pipe client used by other CLI commands to signal changes
- **Embedded JavaScript resources** – client‑side code that receives SSE events and updates the DOM

This design keeps the watch process completely separate from document manipulation, avoiding file‑lock conflicts while enabling instant synchronization.

## Starting the Watch Server

### Command‑Line Invocation

```bash

# Start watching a Word document (default port 26315)

officecli watch mydoc.docx

# Watch a PowerPoint presentation on a custom port

officecli watch slides.pptx --port 8080

# The server prints a URL like: Watch: http://localhost:26315

```

When executed, the command in [[`CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Watch.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs#L34-L70) performs two critical tasks:

1. **Obtains the initial HTML snapshot** via `ResidentClient.TrySend` (fast path through the resident daemon) or falls back to `RenderViaRegistry` using the appropriate document handler (`PowerPointHandler`, `ExcelHandler`, or `WordHandler`)
2. **Launches `WatchServer`** with the rendered HTML and configured port

The server immediately begins listening on `http://localhost:<port>` and injects two embedded resources—`watch‑sse‑core.js` and `watch‑overlay.js`—into every served page.

## Server‑Sent Events Implementation

### Core SSE Infrastructure

`WatchServer` in [[`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs#L81-L91) implements pure **Server‑Sent Events** rather than WebSockets. This choice provides:

- Unidirectional server‑to‑client streaming (no need for client acknowledgments)
- Automatic reconnection handling in browsers
- Simpler state management with HTTP/1.1 compatibility

The server maintains an in‑memory `_currentHtml` cache and a version counter. Each connected browser receives:

```html
<!-- Injected by WatchServer -->
<script>
// watch-sse-core.js establishes EventSource connection
const es = new EventSource('/events');
es.onmessage = (e) => applyPatch(JSON.parse(e.data));
</script>

```

### Real‑Time Update Pipeline

Changes flow through a **named‑pipe IPC channel** identified by `officecli-watch-<hash>`. Any command that modifies a watched document—`set`, `add`, `remove`, `mark`—activates the update chain:

1. **Command invokes `WatchNotifier.Send`** in [[`WatchNotifier.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchNotifier.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchNotifier.cs#L15-L25):

```csharp
// Simplified from WatchNotifier.cs
public static void Send(string pipeName, WatchMessage message)
{
    using var pipe = new NamedPipeClientStream(".", pipeName, 
        PipeDirection.Out);
    pipe.Connect(timeoutMs: 500);
    
    var json = JsonSerializer.Serialize(message);
    pipe.Write(Encoding.UTF8.GetBytes(json));
}

```

2. **WatchServer receives and processes** in [[`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs#L95-L143):

```csharp
// From RunPipeListenerAsync → HandleSinglePipeClientAsync
async Task HandleWatchMessage(WatchMessage msg)
{
    lock (_htmlLock)
    {
        if (msg.FullHtml != null)
            _currentHtml = msg.FullHtml;           // Complete replacement
        else if (msg.Patch != null)
            _currentHtml = PatchSlideInHtml(        // Incremental update
                _currentHtml, msg.SlideNumber, msg.Patch);
        
        _version++;
    }
    
    await BroadcastSseEvent(new 
    {
        action = msg.Action,           // "full", "replace", "add", "remove"
        slide = msg.SlideNumber,       // For PowerPoint context
        html = msg.HtmlFragment,       // When partial content provided
        scroll = msg.ScrollSelector,   // Optional auto-scroll target
        version = _version
    });
}

```

3. **Browser applies DOM changes** through `watch‑sse‑core.js`:
   - **Full refresh** – replaces entire `<body>` content
   - **Slide‑level patch** – updates specific slide element (PowerPoint)
   - **Block‑level diff** – computes and applies minimal changes (Word via `ComputeWordPatches`)
   - **Scroll coordination** – navigates to element matching the provided selector

## Message Types and Incremental Updates

The `WatchMessage` protocol supports multiple update strategies to minimize browser rendering overhead:

| Message Type | Use Case | Data Contents |
|-------------|----------|---------------|
| `FullHtml` | Major structural changes, initial sync | Complete HTML document string |
| `replace` | Single slide replacement (PowerPoint) | Slide number + new HTML fragment |
| `add` | New slide appended (PowerPoint) | Slide number + insert position |
| `remove` | Slide deletion (PowerPoint) | Slide number only |
| `scroll` | Navigation without content change | CSS selector for target element |

**Incremental patching** is implemented in `PatchSlideInHtml`, `AppendSlideToHtml`, and `RemoveSlideFromHtml` methods, which manipulate the cached HTML string using DOM‑aware parsing before re‑serving to browsers.

## Interactive Features: Marks and Annotations

The watch system supports live annotation through the `mark` sub‑command:

```bash

# Highlight a specific paragraph in the live preview

officecli watch mark mydoc.docx "/body/p[3]" --color "#00ff00" --note "Review required"

# Remove a mark

officecli watch unmark mydoc.docx "/body/p[3]"

# List all active marks

officecli watch marks mydoc.docx

```

Mark metadata is stored in‑memory by `WatchServer` and synchronized via the same SSE channel. The `watch‑overlay.js` resource renders visual indicators and selection rectangles directly on the preview.

## Resource Management and Lifecycle

### Idle Shutdown

To prevent orphaned processes, `WatchServer` implements automatic termination in [[`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs)](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs#L55-L73):

```csharp
// Default: 5 minutes (configurable via OFFICECLI_WATCH_IDLE_SECONDS)
async Task RunIdleWatchdogAsync(CancellationToken ct)
{
    while (!ct.IsCancellationRequested)
    {
        await Task.Delay(TimeSpan.FromSeconds(checkInterval), ct);
        
        var idleTime = DateTime.UtcNow - _lastClientActivity;
        if (idleTime > _idleTimeout && _sseClients.IsEmpty)
        {
            _logger.LogInformation("Idle timeout elapsed — shutting down");
            _shutdownCts.Cancel();
            return;
        }
    }
}

```

### Graceful Termination

Signal handlers capture **SIGTERM**, **SIGHUP**, **SIGQUIT**, and **Ctrl‑C**, triggering `StopAsync` which:

1. Cancels all listener tasks
2. Closes TCP sockets gracefully
3. Deletes the on‑disk marker file
4. Releases named pipe resources

## Complete Workflow Example

```bash

# Terminal 1: Start the watch server

$ officecli watch report.docx
Watch: http://localhost:26315

# Terminal 2: Make live edits — each command triggers instant refresh

$ officecli set report.docx --text "Q4 Financial Results"
$ officecli set report.docx --heading "Executive Summary" --level 1
$ officecli watch mark report.docx "/body/h1[1]" --color "#ff6600"

# The browser at localhost:26315 updates automatically after each command

```

## Summary

- **Pure SSE architecture** eliminates WebSocket complexity and browser compatibility issues
- **Named‑pipe IPC** enables any CLI command to push updates without direct file access
- **Incremental patching** minimizes DOM manipulation for better performance
- **In‑memory operation** prevents file‑lock contention with Office applications
- **Automatic lifecycle management** ensures servers terminate cleanly when unused

## Frequently Asked Questions

### How does OfficeCLI watch avoid file‑lock conflicts with Microsoft Office?

The watch server never opens the Office document directly. It receives HTML snapshots either from the resident daemon process or through `RenderViaRegistry` at startup, then operates solely on cached HTML. When modifications occur, other CLI commands handle document access and push updates via named pipe, leaving the watch process isolated from file I/O.

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

Yes. `WatchServer` maintains a concurrent collection of SSE clients (`_sseClients`) and broadcasts events to all connected browsers. Each client receives identical update sequences, though scroll commands may target different viewport positions depending on individual browser state.

### What happens if the watch server crashes or is forcibly terminated?

The named pipe and TCP socket are released by the operating system. Subsequent CLI commands attempting to notify the watch will fail silently after a brief timeout. A new `officecli watch` invocation creates fresh server resources with a new port and pipe name.

### Is the live preview suitable for production document sharing?

No. The watch server binds to `localhost` only and lacks authentication, HTTPS, or access controls. It is designed exclusively for local development workflows. For production sharing, export static HTML via `officecli render` or deploy through proper web infrastructure.