Architecture¶
The pieces¶
muchado words FILE
│
├── host/server.py loopback HTTP server, background thread
│ serves the editor, answers its host calls,
│ runs x2t to open, save and export
│
├── host/bridge.js window.AscDesktopEditor, injected into every page
│ before the editor's own scripts run
│
└── host/window.py a pywebview window pointed at 127.0.0.1
The editor is ordinary web content. It is served over loopback to a WebKit window on your own machine, with one script injected ahead of it.
The seam¶
Upstream's desktop application is a Chromium fork. The editor talks to its host through a single JavaScript object, window.AscDesktopEditor, whose reference implementation is about seven thousand lines of C++ inside CEF.
That object is the entire contract. MuchAdo implements it in JavaScript, backed by a Python server over HTTP — which is why the host is a few thousand lines rather than a browser fork.
The offline editor itself is already in upstream's source: three files, about 1,200 lines, selected at build time by SDK_PLATFORM=desktop. We do not fork it, patch it or reimplement it. We answer it.
What the host actually does¶
| the editor asks | the host does |
|---|---|
| open this document | runs x2t to convert it into the editor's working format |
| here are my changes | appends them to a change log |
| save | runs x2t to merge the working format and the change log back into a document |
| the same, to PDF, then hands the file to the desktop | |
| open / save-as dialog | puts a real native dialog on screen |
| new document | copies a blank from the payload |
| open an external link | hands the URL to your browser |
Every one of these is a small HTTP endpoint. The conversion work is upstream's x2t binary, which is also what upstream uses.
x2t and doctrenderer¶
x2t is the converter. For most formats it is C++ all the way down. For PDF it is not: doctrenderer embeds V8 and runs the editor's own JavaScript layout engine server-side, so the PDF you print is laid out by the same code that drew the screen.
This has a practical consequence. The PDF path needs the real font files and a font index with absolute paths to them — not the obfuscated copies the browser downloads. Get that wrong and it fails inside V8 with a JavaScript type error; get it half-wrong and it segfaults.
The payload¶
The application and the payload are separate artifacts on separate version numbers.
| the Python package | a few hundred KB, on PyPI |
core-<platform>.tar.gz |
58 MB — x2t and the native libraries |
editors.tar.gz |
71 MB — sdkjs and web-apps, Words only |
fonts-core.tar.gz |
4 MB — font sources, not web fonts |
Two things force that split. Generated files hold absolute paths, so they must be produced on the target machine; and doctrenderer reads real .ttf files, so we ship font sources and generate the browser's copies locally at install time.
Design notes¶
Branding is build-time configuration, not a fork. The product name, the logos and the application name written into saved documents are all upstream configuration points.
Our changes to upstream are a patch queue, not a branch. See The patch queue.
One observation is not a diagnosis. The regression harness exists because of a string of confident wrong answers about why the editor would not load. Each was consistent with the evidence and each was wrong.