Simple OOXML Viewer for VS Code
Open .docx, .xlsx, .pptx and .vsdx files in Visual Studio Code, look at them, change the
parts you need to change, and save back into the same file without running them through another
program. Every renderer is a WASM engine bundled in the extension; nothing is uploaded and nothing
converts on a server.
Offline · No telemetry · No AI · No accounts · No ads
Engines, fonts and WASM assets all ship inside the package. Open / edit / save a file and the
extension makes zero network requests: no assistant, no LLM hook, no analytics, no crash
reports, no sign-in, no "phone home".
| Format |
Extensions |
What you get |
| Word |
.docx .docm |
Full paged editor: headers/footers, footnotes, tables, images, tracked changes & comment cards, outline & find, text selection & copy — with lossless in-place save |
| Excel |
.xlsx .xlsm |
Sheet tabs, 347 spreadsheet functions, merged cells, frozen panes, cell editing, selection & copy, zoom, find — the engine parses and writes the .xlsx package itself, so formatting survives the round-trip |
| PowerPoint |
.pptx .pptm |
Slide canvas with masters, images, speaker notes, thumbnail rail, slide navigation, editing |
| Visio |
.vsdx |
Full Visio-style editor: shape stencils with search, Drawing Explorer, Shape Data, Layers, page tabs, connectors, pan & zoom, save |
How it works
- Word — the
@docx-editor.dev paginated word
processor (DOM/ProseMirror), with text shaped through a bundled HarfBuzz WASM asset so line
breaks and page breaks match real font metrics. Saving re-zips the OOXML package selectively:
parts the editor doesn't understand survive the round-trip untouched.
- Excel —
@opencalc/sheet (CasualOffice), a Rust→WASM
spreadsheet engine behind a custom element. It owns the file format: open(bytes) and save()
go straight through its own .xlsx parser and serializer, so formats, styles, merges, panes and
hidden sheets come back unchanged, and inserting or deleting rows rewrites references the way
Excel does.
- PowerPoint — the BetterOffice Rust→WASM engine
rendered through its React editor.
- Visio — a vendored BetterOffice Rust→WASM diagram core behind a native-feeling canvas shell
(the only VS Code extension we know of that opens
.vsdx at all).
- Fonts — metric-compatible substitutes (Calibri→Carlito, Cambria→Caladea, Times New
Roman→Liberation Serif, Arial→Liberation Sans, Courier New→Liberation Mono, plus Noto Arabic /
Hebrew, Gelasio, Inter, Roboto and more) are bundled, so pagination is computed from real
metrics without downloading a single face.
Privacy & offline guarantees
No network access. Opening, editing and saving are pure local computation. This is enforced,
not just intended:
- No egress by construction — every engine receives bytes, not URLs. There is no Google
Fonts lookup to disable (the API no longer exists in the bundled engine), no CDN fallback, no
update checker, no collaboration transport (WebSocket/beacon/EventSource are compile-time
forbidden), no AI or "assist" endpoints.
- Build-time postcheck — every shipped webview bundle is scanned; the build fails if it
contains egress markers or collaboration machinery.
- Strict CSP — the webview runs with
script-src 'nonce-…' plus the extension's own
cspSource (the Excel engine pulls its UI payload with a dynamic import(), which a nonce cannot
cover) and 'wasm-unsafe-eval'; connect-src is limited to the extension's own origin, inside
VS Code's sandbox.
No AI. No model calls, no prompts sent anywhere, no third-party inference SDKs, no data
sharing. What you open never leaves your machine.
The one deliberate exception: simpleOoxml.cjkFonts: "cdn" (default off) fetches
open-licensed CJK font faces from a pinned jsDelivr URL. Off by default, and the only network
path the extension can ever take.
Also: macros in .docm/.xlsm/.pptm are never executed; external hyperlinks open through VS
Code with an http/https/mailto/tel allowlist only.
Usage
Install and open any supported file — the extension registers as the default editor. Use
Open With… to switch back to the binary or text editor at any time.
Ctrl/Cmd + / - / 0 — zoom in / out / reset
Ctrl/Cmd S — save (lossless re-zip for Word)
Ctrl/Cmd F — find in document (where the format supports it)
- Edit the file on disk (e.g. regenerate a report) — the open viewer live-reloads
- Word chrome theme: follow the
simpleOoxml.colorMode setting; the document canvas stays
Word-faithful white in every mode
Extension settings
| Setting |
Default |
Description |
simpleOoxml.editing |
true |
Open documents editable. Turn off to open everything read-only. |
simpleOoxml.forceReadOnly |
false |
Always open read-only, regardless of editing (applies to Word, Excel, PowerPoint). |
simpleOoxml.language |
auto |
Chrome UI language: auto follows VS Code's display language, zh / en force one. Covers Word (engine catalogue) and PowerPoint (built-in menus); the Excel engine's own menu and toolbar strings are hard-coded English, where this setting only switches date / decimal / thousands formatting through its locale. Reopen the editor after changing it. |
simpleOoxml.colorMode |
light |
Word editor chrome color mode: light, dark or system. The document canvas always stays white. |
simpleOoxml.fonts |
bundled |
Use the bundled metric-compatible fonts for Office-accurate layout. off uses system fonts only. |
simpleOoxml.cjkFonts |
off |
cdn fetches CJK faces from the pinned open-source font CDN — the only network access this extension can perform. |
simpleOoxml.initialZoom |
fitPage |
Initial zoom for paged viewers: fitPage, fitWidth or 100. |
Commands
- OOXML Viewer: Show Diagnostics — recent engine lifecycle events, for bug reports
- OOXML Viewer: Copy Diagnostics to Clipboard
- OOXML Viewer: Reload Active Viewer
Known limitations
- Save writes through each engine's serializer; VS Code-level dirty-state markers, hot-exit
backup and external-change conflict prompts are planned — until then, undo history lives in the
open editor.
simpleOoxml.initialZoom: "fitPage" currently behaves like width-fit for Word (the
bundled engine has no page-height fit target yet).
- Visio: linear fill gradients and arc/gap line jumps render; exotic ShapeSheet features may
differ from Visio.
.vsdm/.vstx/.vssx and legacy .vsd are not supported.
- CJK text renders with substituted metrics unless
simpleOoxml.cjkFonts: "cdn" is enabled.
- Excel: the engine is pinned to
@opencalc/sheet@0.0.0. The next tag, 0.0.1, shipped without its
editor.core.js chunk (every UI element failed to construct), so the version is exact, not a
range. The engine's menu labels are English only, and charts, images, conditional formatting and
data validation are previewed as their cached values rather than edited.
- ODF (
.odt/.ods/.odp) and legacy binary formats (.doc/.xls/.ppt/.vsd) are not supported.
Development
npm install
npm run compile # dev build (host + webviews + assets) into out/
npm run watch
npm run typecheck # host + webview tsc
npm test # compile + VS Code integration tests (all four formats)
npm run package # production (minified) build; postchecks enforce the offline contract
npx @vscode/vsce package --no-dependencies # produce the .vsix
npm run build:vsdx-wasm # rebuild the vendored Visio WASM (needs the Rust toolchain)
The production build runs a no-egress / no-collaboration postcheck over every shipped bundle
and fails the build on violations — the build itself is the enforcement (see the
Privacy & offline guarantees section above). docs/SCOPING.md documents the architecture.
.github/workflows/ci.yml runs the host + webview typechecks, the production build (postchecks
included), a VSIX pack with a 26 MB size budget, and the VS Code integration suite under Xvfb.
Package size comes mostly from engine WASM and font binaries. One saving is structural: 16 of the
Excel engine's 24 fonts (Carlito ×4, Liberation ×12) are byte-identical to the copies already
shipped under out/media/assets/, so copyOpencalcAssets() skips them and rewrites those
@font-face URLs to ../assets/<name> — 6.76 MB that used to be packed twice. The build also wipes
out/media/assets/ before copying, so a retired engine's assets cannot linger in the package.
License & third-party attribution
This project is Apache-2.0 © 2026 wasdzy111. It began as a snapshot of ahati's
BetterOffice viewer and has since been rewritten around a different Excel engine; the original
author's notice is retained verbatim in NOTICE, as Apache-2.0 requires. The work also
builds on open-source engines and fonts; the full, authoritative notices ship inside every package
(NOTICE, THIRD-PARTY-NOTICES.md, and the verbatim texts in
licenses/ — one folder per bundled package, including the per-family OFL
texts). Summary:
| Component |
Role |
License |
Copyright |
@docx-editor.dev/core react i18n 2.29.0 |
Word editor |
Apache-2.0 |
© 2026 EigenPal Inc. |
harfbuzzjs |
HarfBuzz text shaping (WASM) |
MIT |
© The HarfBuzz Authors |
@opencalc/sheet 0.0.0 |
Excel engine (Rust→WASM .xlsx parser/serializer + chrome) |
Apache-2.0 |
© The CasualOffice project authors |
@betteroffice/pptx(-react) |
PowerPoint engine |
Apache-2.0 |
© 2026 Elia Hilse, The OpenOOXML Project |
@betteroffice/vsdx (vendored snapshot) |
Visio diagram core + WASM |
Apache-2.0 |
© 2026 Elia Hilse, The OpenOOXML Project |
@betteroffice/fonts |
Bundled metric-compatible faces (Carlito, Caladea, Liberation, Noto, Gelasio, …) |
SIL OFL-1.1, as distributed by the package (licenses/betteroffice-fonts/); Roboto is Apache-2.0 upstream |
© their respective font projects |
react, react-dom |
Webview UI runtime |
MIT |
© Meta Platforms, Inc. and affiliates |
ProseMirror (prosemirror-*), @radix-ui/react-select, fflate, fast-xml-parser, utif2, bidi-js, emf-converter, clsx, class-variance-authority |
Bundled engine/UI dependencies |
MIT / Apache-2.0 (per package) |
per package |
The @docx-editor.dev packages are the renamed continuation of the @eigenpal/docx-editor
packages (same repository, same Apache-2.0 license); the BetterOffice API design in turn derives
from that project. yjs and pdf-lib are declared optional peers of the Word engine for its
collaboration and PDF-export entrypoints — this extension ships neither, and they are not bundled.
| |