🎇Features • 🏠Installation • 🚜Usage • 💻CLI
✅Requirements • 🐳Docker • 🚸Gotchas
Export Excalidraw diagrams to SVG or PNG using a headless browser, using the exact same export process as Excalidraw itself
- ❔ What: Uses 🎭 playwright to run a headless firefox browser to export Excalidraw diagrams to svg/png files. Using a browser bypasses certain bugs that happen with other projects that attempt to export by emulating the DOM (without a browser).
- Why:
- To allow automated export of Excalidraw diagrams to svg/png files via the command line.
- Currently, Excalidraw can only be exported by a human clicking on the "Export image" button.
- Addresses/mitigates
excalidraw/excalidraw#1261
excalidraw CLI #1261
which is an open feature request on the Excalidraw project. - Addresses/mitigates
JRJurman/excalidraw-to-svg#6
Error rendering edge-labels. #6
. - Addresses/mitigates
Timmmm/excalidraw_export#6
Error rendering edge-labels. #6
.
- 🤝 Related Projects
- ⭐⭐⭐⭐⭐
JRJurman/excalidraw-to-svg
uses jsdom to simulate the DOM, then runs Excalidraw+react in nodejs, loads
the diagram files and exports them.
- Comparison:
JRJurman/excalidraw-to-svg
is faster and more efficient than excalidraw-brute-export-cli. However, excalidraw-brute-export-cli is a "brute force" approach to exporting Excalidraw diagrams, and in some ways might be more reliable.
- Comparison:
- ⭐⭐⭐⭐⭐
Timmmm/excalidraw_export
similar to
JRJurman/excalidraw-to-svg
but simplifies the code and also embeds SVG fonts.- Comparison:
Timmmm/excalidraw_export
is faster and more efficient than excalidraw-brute-export-cli. However, excalidraw-brute-export-cli is a "brute force" approach to exporting Excalidraw diagrams, and in some ways might be more reliable.
- Comparison:
- ⭐⭐⭐⭐⭐
JRJurman/excalidraw-to-svg
uses jsdom to simulate the DOM, then runs Excalidraw+react in nodejs, loads
the diagram files and exports them.
- Export Excalidraw diagrams to SVG or PNG using a headless browser, using the exact same export process as Excalidraw itself.
- Can point to any excalidraw instance.
- Ability to change timeouts.
- Debugging: Ability to take screenshots at each step.
# Install globally from npm registry.
npm install -g excalidraw-brute-export-cli
# Or install globally, direct from GitHub:
npm install -g https://github.com/realazthat/excalidraw-brute-export-cli.git#v0.4.0
# Might prompt for root.
npx playwright install-deps
npx playwright install firefox
Example:
npx excalidraw-brute-export-cli \
-i ./examples/simple.excalidraw \
--background 1 \
--embed-scene 0 \
--dark-mode 0 \
--scale 1 \
--format svg \
-o "./examples/simple_example_output.svg"
ls "./examples/simple_example_output.svg"
And the resulting image (svg):
-
https://excalidraw.com is a moving target as it gets updated. You can alternatively run your own excalidraw at a specific git tag, and point to it with the
--url
option, for more reproducible results.- Unfortunately, as of
2024/05/05
the Excalidraw project doesn't regularly update its docker image (See https://hub.docker.com/r/excalidraw/excalidraw/tags, excalidraw/issues/7893, excalidraw/issues/7403). - Unfortunately, as of
2024/05/05
the Excalidraw project's Dockerfile is frequently broken (See excalidraw/issues/7582, excalidraw/pull/7806, excalidraw/pull/7430, excalidraw/pull/7502).- According to
excalidraw/issues/7582#issuecomment-1900651112
v0.15.0
(from2023/04/18
) is the last tag that builds.
- According to
excalidraw/issues/7582#issuecomment-1900651112
- Unfortunately, as of
-
Here is how to run your own Excalidraw instance at Excalidraw tag
v0.15.0
:
# First build the image. Do this once.
git clone https://github.com/excalidraw/excalidraw.git
cd excalidraw
git checkout "v0.15.0"
docker build -t "my-excalidraw-image:v0.15.0" .
# Now we'll run Excalidraw in a container instance.
# Delete the old instance if it exists.
docker rm -f "your-instance-name" || true
# Replace 59876 with your desired port.
docker run -dit --name "your-instance-name" -p 59876:80 "my-excalidraw-image:v0.15.0"
# Visit your instance at http://localhost:59876
# Use the --url option to point to your instance.
npx excalidraw-brute-export-cli \
...
--url "http://localhost:59876" \
...
# Or, set `EXCALIDRAW_BRUTE_EXPORT_CLI_URL` in your environment and leave out
# the --url option.
- Tested with latest version of https://excalidraw.com as of
2024/05/05
, and Excalidraw tagv0.15.0
for more consistent output, and testing. - Supported Node versions:
>=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=21.0.0 <22.0.0 || >=22.0.0 <23.0.0
(See ./package.json). These versions were chosen from current supported and upcoming versions of node, from Node.js: Previous Releases. - Tested Node versions on GitHub Actions:
["18.20.2","20.12.1","21.7.3","22.0.0"]
.
- WSL2 Ubuntu 20.04, Node
v20.12.1
using Excalidraw at tagv0.15.0
.
Docker images are published to [ghcr.io/realazthat/excalidraw-brute-export-cli][49] at each tag.
# Use the published images at ghcr.io/realazthat/snipinator.
# /data in the docker image is the working directory, so paths are simpler.
docker run --rm --tty \
-u "$(id -u):$(id -g)" \
-v "${PWD}:/data" \
ghcr.io/realazthat/excalidraw-brute-export-cli:v0.4.0 \
-i ./examples/simple.excalidraw \
--background 1 \
--embed-scene 0 \
--dark-mode 0 \
--scale 1 \
--format svg \
-o "./examples/simple_example_output.svg"
ls "./examples/simple_example_output.svg"
If you want to build the image yourself, you can use the Dockerfile in the repository.
docker build -t my-excalidraw-brute-export-cli-image .
# /data in the docker image is the working directory, so paths are simpler.
docker run --rm --tty \
-u "$(id -u):$(id -g)" \
-v "${PWD}:/data" \
my-excalidraw-brute-export-cli-image \
-i ./examples/simple.excalidraw \
--background 1 \
--embed-scene 0 \
--dark-mode 0 \
--scale 1 \
--format svg \
-o "./examples/simple_example_output.svg"
ls "./examples/simple_example_output.svg"
- Sometimes playwright times out.
- Mitigations:
- Increase the timeout with the
--timeout
option. - Run the command again.
- Increase the timeout with the
- If this is a persistent problem, please open an issue here and upload the diagram (zip it if necessary).
- Mitigations:
We use SemVer for versioning. For the versions available, see the tags on this repository.
This project is licensed under the MIT License - see the ./LICENSE.md file for details.
-
For running
pre.sh
(Linux-like environment).-
From ./.github/dependencies.yml, which is used for the GH Action to do a fresh install of everything:
bash: scripts. findutils: scripts. grep: tests. xxd: tests. git: scripts, tests. xxhash: scripts (changeguard). rsync: out-of-directory test. expect: for `unbuffer`, useful to grab and compare ansi color symbols. jq: dependency for [yq](https://github.com/kislyuk/yq), which is used to generate the README; the README generator needs to use `tomlq` (which is a part of `yq`) to query `pyproject.toml`.
-
Requires
pyenv
, or an exact matching version of python as in scripts/.python-version (which is currently3.8.18
). -
jq
, (installation) required for yq, which is itself required for our ./README.md generation, which usestomlq
(from the yq package) to include version strings from ./scripts/pyproject.toml. -
act (to run the GH Action locally):
- Requires nodejs.
- Requires Go.
- docker.
-
Generate animation:
- docker
-
Tests
- docker, to serve Excalidraw.
-
- (Optionally) Fork the
develop
branch. - If the .github/demo.gif will change, run
bash ./scripts/generate-animation.sh
, this will generate a new .github/demo.gif.- Sanity-check the animation visually!
- Unfortunately, every run will make a unique gif, please don't stage this file unless it changes due to some feature change or somesuch.
- Stage your files: e.g
git add path/to/file.py
. bash ./scripts/pre.sh
, this will format, lint, and test the code.git status
check if anything changed (generated README.md for example), if so,git add
the changes, and go back to the previous step.git commit -m "..."
.- Make a PR to
develop
(or push to develop if you have the rights).
These instructions are for maintainers of the project.
- In the
develop
branch, runbash ./scripts/pre.sh
to ensure everything is in order. - In the
develop
branch, bump the version in package.json, following semantic versioning principles. Runbash ./scripts/pre.sh
to ensure everything is in order.- If anything got generated (e.g README or terminal output images), you will have to stage those.
- In the
develop
branch, commit these changes with a message like"Prepare release X.Y.Z"
. (See the contributions section above). - Merge the
develop
branch into themaster
branch:git checkout master && git merge develop --no-ff
. master
branch: Tag the release: Create a git tag for the release withgit tag -a vX.Y.Z -m "Version X.Y.Z"
.- Publish to NPM: Publish the release to NPM with
bash ./scripts/deploy-to-npm.sh
. - Push to GitHub: Push the commit and tags to GitHub with
git push && git push --tags
. - The
--no-ff
option adds a commit to the master branch for the merge, so refork the develop branch from the master branch:git checkout develop && git merge master
. - Push the develop branch to GitHub:
git push origin develop
.