OfficeCLI Watch Mode Live HTML Preview Auto-Refresh: How It Works
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 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 |
| Watch Server | SSE relay, state management, idle timeout, graceful shutdown | src/officecli/Core/Watch/WatchServer.cs |
| Client Overlay | Browser-side rendering, DOM patching, UI interactions | src/officecli/Resources/watch-sse-core.js, 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 (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:
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:
- Resident process first — Calls
ResidentClient.TrySendwithmode=htmlandJson=trueto leverage an already-running CLI instance - Direct handler fallback — If no resident exists, opens the file via
DocumentHandlerFactory.Openand renders throughRenderViaRegistry
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 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 clientsGetSelection()— Query current user selectionAddMark()/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:
// 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:
// 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:
// 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
# 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:
# 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:
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 |
12-34 | CLI command definition, argument parsing, orchestration |
src/officecli/Core/Watch/WatchServer.cs |
16-30, 106-119 | SSE server, state management, idle timeout logic |
src/officecli/Core/Watch/WatchNotifier.cs |
Full file | Named-pipe client for inter-process communication |
src/officecli/Resources/watch-sse-core.js |
Full file | SSE connection handling, DOM diff/patching |
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) handles argument parsing and initial rendering via resident process or direct handler fallback - The watch server (
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,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 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →