# How the OfficeCLI `watch` Command Enables Live Preview with Auto-Refresh

> Learn how the OfficeCLI watch command creates live previews with auto-refresh. It uses SSE to serve document previews and automatically updates your browser on file changes.

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

---

**The `watch` command starts an HTTP server with Server-Sent Events (SSE) that serves a document preview and automatically refreshes the browser whenever OfficeCLI modifies the file.**

The OfficeCLI `watch` command provides developers with real-time visual feedback when editing Office documents programmatically. By combining a lightweight HTTP server, embedded JavaScript clients, and an inter-process notification system, the tool eliminates the need to manually reload browsers after each change.

## Command Architecture and Entry Point

The `watch` command is declared in [`src/officecli/CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs). The command description explicitly states its purpose: *"Start a live preview server that refreshes when officecli modifies the document (external edits are not detected)"* with subcommands for mark/unmark/marks/goto operations on the running preview.

When executed, the command instantiates `WatchServer` and begins the live preview lifecycle. The implementation deliberately limits detection to CLI-initiated changes only, avoiding the complexity of file system watchers while ensuring predictable behavior.

## HTTP and Server-Sent Events Infrastructure

The core server implementation resides in [`src/officecli/Core/Watch/WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs). On startup, the server loads two embedded JavaScript resources that establish the client-side refresh mechanism:

```csharp
// From WatchServer.cs lines 90-99
var sseCore = LoadEmbeddedResource("watch-sse-core.js");
var overlayScript = LoadEmbeddedResource("watch-overlay.js");

```

These scripts—which correspond to [`Resources/watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Resources/watch-sse-core.js) and [`Resources/watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Resources/watch-overlay.js)—perform two critical functions:

- **SSE connection management**: Opens and maintains a persistent connection to the server's `/events` endpoint
- **DOM injection**: Receives HTML payloads and updates the preview without full page reloads

## Port Discovery and Process Coordination

The watch server creates a predictable marker file under `$TMPDIR` with the naming pattern `officecli-watch-<hash>.port`. This file stores the TCP port number where the HTTP server listens.

```csharp
// WatchServer.cs lines 186-204
var markerPath = Path.Combine(
    Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp",
    $"officecli-watch-{ComputeHash(documentPath)}.port");
File.WriteAllText(markerPath, port.ToString());

```

This marker file enables **process discovery**: other CLI instances can determine if a watch server is already running for a given document and route notifications appropriately.

## The Notification and Refresh Cycle

Live refresh depends on `WatchNotifier` in [`src/officecli/Core/Watch/WatchNotifier.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchNotifier.cs). When any mutating command completes—such as `set`, `add`, or `remove`—it calls:

```csharp
// WatchNotifier.cs lines 15-26
public static async Task NotifyAsync(string documentPath, string html)
{
    var pipeName = GetPipeNameFromMarker(documentPath);
    try {
        using var pipe = new NamedPipeClientStream(".", pipeName, 
            PipeDirection.Out);
        await pipe.ConnectAsync(100); // 100ms timeout
        await WriteMessageAsync(pipe, html);
    }
    catch {
        // Fire-and-forget: ignore if no watcher running
    }
}

```

This **fire-and-forget** design ensures the CLI remains non-blocking. The notification travels through a named pipe to the watch server, which then broadcasts the HTML payload to all connected browser clients via SSE.

```bash

# Terminal 1: Start live preview

officecli watch myDoc.docx

# Terminal 2: Modify document—browser auto-refreshes

officecli set myDoc.docx /body --prop "font-size=14pt"
officecli add myDoc.docx /body/paragraph "New content"

```

## Graceful Shutdown and Cleanup

The watch server monitors a `CancellationTokenSource` (`_cts`) that triggers on multiple termination paths:

- Explicit `unwatch` command invocation
- SIGINT/SIGTERM signals
- Dispose pattern from the hosting context

```csharp
// WatchServer.cs lines 437-494
_cts.Token.Register(() => {
    _httpListener?.Stop();
    File.Delete(markerPath);
    _namedPipeServer?.Dispose();
});

```

Removing the marker file on shutdown allows subsequent `watch` invocations to start fresh servers without port conflicts.

## Practical Usage Examples

Start a live preview for any supported Office document:

```bash
officecli watch report.docx
officecli watch presentation.pptx
officecli watch workbook.xlsx

```

Control the running preview with subcommands:

```bash
officecli watch report.docx mark /body/paragraph[3]    # Add visual marker

officecli watch report.docx goto /body/table[1]        # Scroll to element

officecli unwatch report.docx                          # Stop server

```

When running in CI or automated environments, omit `unwatch`—the server will terminate when the parent process exits, though explicit cleanup is recommended.

## Summary

- **HTTP + SSE server**: `WatchServer` serves preview content and pushes updates via Server-Sent Events
- **Embedded JavaScript clients**: [`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) handle browser-side refresh without page reloads
- **Port marker files**: Enable cross-process discovery of running watch servers via `$TMPDIR/officecli-watch-*.port`
- **Named pipe notifications**: `WatchNotifier` transmits rendered HTML through fire-and-forget messages
- **Graceful lifecycle**: Cancellation token coordination ensures clean shutdown across normal and signal-based termination paths

## Frequently Asked Questions

### Why doesn't external file editing trigger auto-refresh?

The `watch` command only detects modifications made through OfficeCLI itself. According to the source in [`CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Watch.cs), this is an intentional design choice to avoid file system polling complexity and ensure the preview always reflects the CLI's internal document state.

### What happens if the watch server crashes or is killed?

The marker file in `$TMPDIR` may persist temporarily, but subsequent `watch` commands will detect stale markers through connection attempts and replace them. The `unwatch` command includes cleanup logic for orphaned marker files.

### Can multiple documents be watched simultaneously?

Yes. Each document receives a unique hash-derived marker filename and independent TCP port. The `WatchServer` constructor takes a document path parameter, and separate named pipes isolate communication channels between instances.

### Is there a performance cost to leaving watch running?

Minimal. The server uses async I/O with low overhead when idle. The SSE connection maintains an open TCP socket but transmits data only when `WatchNotifier` sends updates, making it suitable for long-running development sessions.