How OfficeCLI Watch Command Provides Live Browser Preview and Auto-Refresh
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) that hosts the SSE endpoint and maintains the HTML snapshot, and the WatchNotifier (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 (lines 34–70):
- Attempts daemon communication — calls
ResidentClient.TrySendto request HTML from a runningofficecliresident process - Falls back to direct rendering — if no daemon responds, opens the document via the appropriate handler (
PowerPointHandler,ExcelHandler, orWordHandler) and callsRenderViaRegistry - Launches WatchServer — starts the TCP listener on
http://localhost:26315(default port, configurable via environment)
# 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 and watch-overlay.js.
Server-Sent Events: The Update Mechanism
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:
<!-- 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 (lines 15–25). The notifier:
- Computes a hash of the watched file path to locate the correct named pipe
- Opens
officecli-watch-<hash>(platform-specific named pipe / Unix domain socket) - 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 |
// 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 runs RunPipeListenerAsync continuously. When HandleSinglePipeClientAsync receives a message, it delegates to HandleWatchMessage (lines 118–143) which:
- For full refreshes: Replaces
_currentHtmlentirely - For patches: Applies mutations via
PatchSlideInHtml,AppendSlideToHtml, orRemoveSlideFromHtml
After updating the cached snapshot, the server:
- Increments an internal version counter
- Calls
SendSseEventto broadcast to all connected browsers - Includes the
scrollselector if provided, enabling automatic navigation to changed content
# 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 handles three update types:
full: Replacesdocument.body.innerHTMLwith the complete new snapshotreplace/add/remove: Patches specific slide elements for PowerPoint, or computesComputeWordPatchesfor Word documents to apply block-level diffsscroll: Executesdocument.querySelector(msg.scroll).scrollIntoView()when a selector is provided
The 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 (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:
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) |
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) |
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) |
IPC client for broadcasting updates from other commands |
Resources/watch-sse-core.js (embedded) |
Browser-side SSE connection, DOM patching, scroll handling |
Resources/watch-overlay.js (embedded) |
UI decorations, mark visualization, selection tools |
src/officecli/Core/ResidentClient.cs / ResidentServer.cs |
Optional daemon mode for lock-free initial renders |
Summary
- OfficeCLI watch creates a self-contained SSE server on
localhost:26315that 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.
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 →