Build and push server image / build-and-push (push) Successful in 1m57s
@excalidraw/utils is a browser bundle -- it references devicePixelRatio, location, matchMedia etc. as bare globals the way inline <script> code would, not as window.foo. Only copying window/document/navigator onto Node's global left everything else undefined, so the very first bare reference threw a ReferenceError as soon as the module loaded. Copy jsdom's entire window onto global instead, enable pretendToBeVisual so jsdom actually populates devicePixelRatio/requestAnimationFrame, and stub matchMedia since jsdom doesn't implement it at all.
45 lines
2.3 KiB
Markdown
45 lines
2.3 KiB
Markdown
# whiteboard-render
|
|
|
|
Local sidecar for whiteboard frame mode -- see `server.js`'s own header
|
|
comment for the full "why Node, why not a headless browser" reasoning.
|
|
Not an independently deployed service: it runs as a second process
|
|
inside the main server's container (`../Dockerfile` installs Node,
|
|
`../start.sh` launches this in the background before `exec`-ing
|
|
uvicorn), reachable only at `127.0.0.1:3001` from the Python process in
|
|
that same container.
|
|
|
|
**Not runtime-tested against a real `npm install` during development**
|
|
-- this environment had no Node.js/npm available, only the npm registry
|
|
API (used to verify the dependency versions/licenses in `package.json`
|
|
actually exist and resolve). The code is written carefully against each
|
|
library's documented API (`@excalidraw/utils`'s `exportToSvg`,
|
|
`@resvg/resvg-js`'s `Resvg` class), but `docker compose build`+actually
|
|
running it is the first time this has executed end to end -- and the
|
|
first real run did in fact crash: `@excalidraw/utils`'s bundle touches
|
|
bare browser globals (`devicePixelRatio`, `location`, `matchMedia`, ...)
|
|
the same way inline `<script>` code in a real page would, not as
|
|
`window.foo` -- jsdom only puts those *on* `dom.window`, so only
|
|
copying `window`/`document`/`navigator` onto Node's `global` (the
|
|
original version of this file) left everything else undefined. Fixed
|
|
by copying jsdom's entire `window` onto `global` plus a `matchMedia`
|
|
stub (jsdom doesn't implement it at all). If another crash like this
|
|
shows up, it's almost certainly the same shape -- another bare global
|
|
the bundle expects that this file hasn't stubbed yet. One other gap
|
|
worth knowing about going in: jsdom's `<canvas>` has no real 2D
|
|
rendering context (no `node-canvas` installed), so if Excalidraw's text
|
|
measurement path depends on `canvas.getContext('2d').measureText(...)`
|
|
rather than pure SVG/font-metrics math, that could be a next thing to
|
|
watch for -- not something confirmed broken, just not yet exercised.
|
|
|
|
## Local development (if you have Node 20.19+/22.13+ installed)
|
|
|
|
```sh
|
|
cd render-service
|
|
npm install
|
|
npm start # listens on 127.0.0.1:3001
|
|
curl -X POST http://127.0.0.1:3001/render \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"elements": [], "width": 800}' \
|
|
-o /tmp/test.png # an empty scene -- just checks the service comes up and returns a valid PNG
|
|
```
|