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):

  1. Attempts daemon communication — calls ResidentClient.TrySend to request HTML from a running officecli resident process
  2. Falls back to direct rendering — if no daemon responds, opens the document via the appropriate handler (PowerPointHandler, ExcelHandler, or WordHandler) and calls RenderViaRegistry
  3. 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:

  1. Computes a hash of the watched file path to locate the correct named pipe
  2. Opens officecli-watch-<hash> (platform-specific named pipe / Unix domain socket)
  3. 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 _currentHtml entirely
  • For patches: Applies mutations via PatchSlideInHtml, AppendSlideToHtml, or RemoveSlideFromHtml

After updating the cached snapshot, the server:

  1. Increments an internal version counter
  2. Calls SendSseEvent to broadcast to all connected browsers
  3. Includes the scroll selector 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: Replaces document.body.innerHTML with the complete new snapshot
  • replace/add/remove: Patches specific slide elements for PowerPoint, or computes ComputeWordPatches for Word documents to apply block-level diffs
  • scroll: Executes document.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:26315 that 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →