# OfficeCLI Watch Mode Live HTML Preview Auto-Refresh: How It Works

> Discover OfficeCLI watch mode for live HTML preview and auto-refresh. See your Office documents instantly update in the browser as you edit them. Learn more at iOfficeAI/OfficeCLI.

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

---

**The `officecli watch` command launches a local server that renders Office documents as live-updating HTML in your browser, automatically refreshing whenever the document changes.**

OfficeCLI's watch mode transforms the traditionally static experience of viewing Word, Excel, and PowerPoint files into a fluid, edit-and-see workflow. Whether you're tweaking a report or collaborating on a presentation, the auto-refresh mechanism keeps your browser preview synchronized with every CLI modification—no manual reload required. This article examines the complete implementation as found in the [iOfficeAI/OfficeCLI](https://github.com/iOfficeAI/OfficeCLI) repository.

## Architecture Overview: Three Distinct Layers

The watch mode system is deliberately partitioned into three loosely-coupled layers that communicate through well-defined interfaces:

| Layer | Core Responsibility | Primary Source File |
|-------|---------------------|-------------------|
| **CLI Orchestration** | Parse arguments, render initial HTML, spawn the watch server | [`src/officecli/CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs) |
| **Watch Server** | SSE relay, state management, idle timeout, graceful shutdown | [`src/officecli/Core/Watch/WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs) |
| **Client Overlay** | Browser-side rendering, DOM patching, UI interactions | [`src/officecli/Resources/watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js), [`watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-overlay.js) |

This separation ensures the server never blocks on document I/O while the client handles all presentation concerns.

## Layer 1: CLI Orchestration in CommandBuilder.Watch.cs

The entry point for watch mode resides in [`src/officecli/CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs) (lines 12-34). This orchestration layer handles three critical responsibilities:

### Argument Parsing and Defaults

The command accepts a file path and an optional `--port` parameter defaulting to **26315**:

```bash
officecli watch MyReport.docx          # Uses default port 26315

officecli watch MyReport.docx --port 3000  # Custom port

```

### Initial HTML Rendering Strategy

The orchestrator attempts rendering through two fallback mechanisms:

1. **Resident process first** — Calls `ResidentClient.TrySend` with `mode=html` and `Json=true` to leverage an already-running CLI instance
2. **Direct handler fallback** — If no resident exists, opens the file via `DocumentHandlerFactory.Open` and renders through `RenderViaRegistry`

This dual-path approach minimizes cold-start latency for frequently accessed documents.

### Watch Server Lifecycle Management

A `WatchServer` instance is constructed with:
- The target file path
- The selected port
- The pre-rendered initial HTML (optional but recommended)

The entire operation is wrapped in `SafeRun` to guarantee clean shutdown on `SIGINT` or `SIGTERM`.

## Layer 2: The SSE Relay Server

[`src/officecli/Core/Watch/WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs) implements a **pure Server-Sent Events relay** with a critical architectural constraint: **it never opens or reads the document file directly**. This design choice isolates file locking concerns and enables multi-process safety.

### Named-Pipe IPC Architecture

HTML updates flow from the CLI to browsers through this pipeline:

```

CLI Process → Named Pipe (WatchNotifier) → WatchServer → SSE → Browser

```

The `WatchNotifier` static class provides non-blocking methods:
- `SendRefresh()` — Push new HTML to all connected clients
- `GetSelection()` — Query current user selection
- `AddMark()` / `RemoveMark()` — Manage document annotations

### In-Memory State Management

The server maintains two concurrent data structures protected by dedicated locks:

| State | Purpose | Lock Object |
|-------|---------|-------------|
| `_currentSelection` | User's active text/element selection | `_selectionLock` |
| `_currentMarks` | Persistent annotations with colors and paths | `_marksLock` |

These structures enable fast queries without filesystem access. The `marks` system allows users to flag specific document locations for later navigation.

### Idle Timeout and Automatic Shutdown

The `ResolveIdleTimeout` method (lines 106-119) implements configurable inactivity detection:

```csharp
// Default: 5 minutes of inactivity triggers shutdown
// Override via environment variable:
set OFFICECLI_WATCH_IDLE_SECONDS=600  # 10 minutes

```

All shutdown paths—idle timeout, `unwatch` command, or OS signals—converge on a single `_shutdownTask` guarded by `_shutdownLock`, ensuring the underlying `TcpListener` is properly disposed.

### Embedded Script Injection

The server lazily loads two JavaScript resources via `LoadWatchResource` and injects them into every served page:

```csharp
// SseScriptContent property in WatchServer.cs
string SseScriptContent => LoadWatchResource("watch-sse-core.js") 
                        + LoadWatchResource("watch-overlay.js");

```

This guarantees clients receive compatible client-side logic without external dependencies.

## Layer 3: Client-Side Overlay System

The browser-facing code splits responsibilities across two complementary scripts in `src/officecli/Resources/`.

### watch-sse-core.js: Foundation Layer

This script establishes the **EventSource** connection and handles the core update cycle:

```javascript
// Creates: window._watchEs (the EventSource instance)
// Invokes: window._watchReapplyHook after each DOM mutation

const eventSource = new EventSource('/events');
eventSource.addEventListener('html', (e) => {
    const patch = JSON.parse(e.data);
    applyDiffPatch(document.documentElement, patch);
    if (window._watchReapplyHook) window._watchReapplyHook();
});

```

The **diff-patch algorithm** minimizes DOM manipulation by computing structural changes rather than replacing entire documents, preserving scroll position and user focus where possible.

### watch-overlay.js: Interaction Layer

This secondary script registers `window._watchReapplyHook` to implement:

- **Selection tracking** — Captures user text/element selections and reports them to the server
- **Mark rendering** — Visual annotations with customizable colors
- **Rubber-band drawing** — Visual selection aids
- **CSS injection** — Theming and accessibility adjustments
- **Context menu** — Quick access to mark, goto, and unmark actions

Communication back to the server uses the same named-pipe infrastructure for commands like `mark`, `goto`, and `unmark`.

## Practical Usage Examples

### Starting Watch Sessions

```bash

# Default configuration

officecli watch QuarterlyReport.docx

# PowerPoint with custom port

officecli watch ProductDemo.pptx --port 8080

# Excel workbook

officecli watch FinancialModel.xlsx

```

### Runtime Interaction Commands

Once watching, manipulate the live session from another terminal:

```bash

# Add a yellow mark at a specific XPath

officecli watch QuarterlyReport.docx mark --color yellow --path /body/p[3]

# List all active marks

officecli watch QuarterlyReport.docx marks

# Navigate browser to specific element

officecli watch QuarterlyReport.docx goto --path /body/table[1]

# Terminate the watch server

officecli unwatch QuarterlyReport.docx

```

### Programmatic Integration

Push custom HTML updates from C# code using the same named-pipe interface:

```csharp
using OfficeCLI.Core.Watch;

// Push a rendered snapshot to active watchers
bool delivered = WatchNotifier.SendRefresh(
    filePath: @"C:\Docs\Contract.docx",
    html: renderedHtmlString,
    version: documentVersion);  // Version enables client-side conflict detection

```

## Key Implementation Files

| File Path | Lines of Interest | Purpose |
|-----------|-------------------|---------|
| [`src/officecli/CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/CommandBuilder.Watch.cs) | 12-34 | CLI command definition, argument parsing, orchestration |
| [`src/officecli/Core/Watch/WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchServer.cs) | 16-30, 106-119 | SSE server, state management, idle timeout logic |
| [`src/officecli/Core/Watch/WatchNotifier.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Watch/WatchNotifier.cs) | Full file | Named-pipe client for inter-process communication |
| [`src/officecli/Resources/watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-sse-core.js) | Full file | SSE connection handling, DOM diff/patching |
| [`src/officecli/Resources/watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Resources/watch-overlay.js) | Full file | Selection, marks, and UI overlay functionality |

## Summary

- **OfficeCLI watch mode** provides live HTML preview with automatic refresh for Word, Excel, and PowerPoint documents through a three-layer architecture
- The **CLI orchestration layer** ([`CommandBuilder.Watch.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Watch.cs)) handles argument parsing and initial rendering via resident process or direct handler fallback
- The **watch server** ([`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs)) acts as a pure SSE relay using named-pipe IPC, with in-memory state for selections and marks
- **Idle timeout** (default 5 minutes, configurable via `OFFICECLI_WATCH_IDLE_SECONDS`) ensures automatic cleanup of abandoned sessions
- **Client-side scripts** ([`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js), [`watch-overlay.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-overlay.js)) handle DOM diffing, state restoration, and user interaction overlays
- All components communicate through **non-blocking APIs** that fail silently, maintaining CLI stability regardless of watch server availability

## Frequently Asked Questions

### How does OfficeCLI avoid file locking conflicts in watch mode?

The watch server **never opens the document file directly**. Instead, it receives pre-rendered HTML through a named-pipe IPC mechanism from the CLI process. This design ensures the server can run continuously without locking the original `.docx`, `.xlsx`, or `.pptx` file, allowing other applications—including the Office desktop suite—to modify the document freely.

### Can I run multiple watch sessions for different documents simultaneously?

Yes. Each `officecli watch` invocation spawns an independent server on a distinct port. The default port is 26315, but you can specify alternatives with `--port`. The `WatchNotifier` class routes messages to the correct server instance based on the file path parameter, enabling parallel watch sessions without interference.

### What happens if the browser loses connection during a watch session?

The client-side [`watch-sse-core.js`](https://github.com/iOfficeAI/OfficeCLI/blob/main/watch-sse-core.js) automatically attempts to reconnect using the browser's native EventSource retry mechanism. When the connection restores, the server pushes the current HTML state immediately, bringing the browser back to synchronization. No manual refresh is required, though any transient selection state may need to be re-established by the user.

### How do I extend the idle timeout for long-running watch sessions?

Set the `OFFICECLI_WATCH_IDLE_SECONDS` environment variable before launching the watch command. The `ResolveIdleTimeout` method in [`WatchServer.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/WatchServer.cs) reads this value, defaulting to 300 seconds (5 minutes) if unspecified. Set to 0 or a large value like 86400 (24 hours) for extended development sessions.