Add board listing, draw.io export and bulk backup - #144
Open
marnusvanwyk459 wants to merge 2 commits into
Open
Conversation
Adds four things on top of the existing frame/board export, plus a fix for a bug that made most exports fail against real boards. Fix: the wait for the Miro SDK was hard-coded to 3 seconds, but the SDK routinely takes ~8.5 seconds to appear on a real board, so every export that addresses individual objects (all JSON export, all frame-scoped export) failed with "Miro SDK failed to load in 3000 ms". The timeout is now configurable via sdkLoadTimeoutMs / --sdk-timeout and defaults to 30 seconds. Whole-board SVG export also now waits for the SDK when it can, falling back to the previous best-effort behaviour when it never loads, which measurably increases the detail captured. The CLI also now exits non-zero when an export fails; previously it printed the error and exited 0. MIRO_TOKEN / .env support: the token can come from the environment instead of --token on every invocation, loaded via Node's own process.loadEnvFile so no dependency is added. An explicit token wins over the environment, which wins over .env. list-boards: lists the boards a token can reach, using the same endpoint as the Miro dashboard. No browser is involved, so it returns in about a second. Note that Miro's offset paging on this endpoint is unreliable (following nextLink can silently stop early), so the whole list is requested in one page by default and results are de-duplicated by ID. draw.io export (-e drawio, and an offline `convert` command): converts board objects to native mxGraph, not an embedded image. Shapes, sticky notes, text and frames become draw.io cells and connectors become edges with real source/target, exit/entry anchors and captions. Three things Miro does not record the way draw.io needs them, each documented in the README: sticky notes carry no font size (Miro fits text to the note), connectors carry no waypoints (so a bare curved=1 renders straight), and square sticky notes declare a box 14.57% taller than it is wide, the extra height being reserve for overflowing text rather than part of the note. Taking that box at face value makes vertically adjacent notes overlap even though Miro draws a gap, so square notes are emitted square. backup: walks the whole board list into a timestamped directory, one directory per board, with a manifest. Serial with a configurable pause, retries with a capped exponential backoff, and --resume to continue an interrupted run. Board titles are sanitised for use as directory names. Tests: 60 unit tests covering token resolution, board listing, the draw.io conversion and the backup helpers, all offline. The live tests skip themselves when MIRO_TOKEN is not set. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
marnusvanwyk459
force-pushed
the
feat/list-boards-drawio-export-and-backup
branch
from
August 14, 2026 11:43
ffb3ebd to
2243654
Compare
The build appended the shebang with `echo "#!/usr/bin/env node\n$(cat ./build/cli.js)" > ./build/cli.js`, which relies on POSIX command substitution. Under the shell npm uses on Windows it silently no-ops, so build/cli.js is produced without a shebang and the installed bin fails to execute. Replace it with a `node -e` step that prepends the shebang only when it is missing, so the build behaves the same on every platform and stays idempotent across rebuilds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hi — thanks for this tool, it does the hard part (getting full-detail vector out of Miro) really well. We're using it to archive a few hundred boards, and this is what we needed on top. Happy to split it into smaller PRs if you'd prefer to take it in pieces; it's grouped into independent commits' worth of work and I've described each part separately below.
A bug worth looking at first
The wait for the Miro SDK is hard-coded to 3 seconds:
On every real board I tested,
window.mirotakes about 8.5 seconds to appear (cmd.board.apiat ~4.7s, SDK at ~8.5s). So anything that addresses individual objects — all JSON export, all frame-scoped export — fails with:The timeout is now an option (
sdkLoadTimeoutMs,--sdk-timeout), defaulting to 30 seconds. The existing 3-second best-effort wait is kept for the case where the SDK isn't required.Related: whole-board SVG export previously skipped the SDK wait entirely. It now waits when it can and falls back to the old best-effort path when the SDK never loads, so the "buggy board" case in the test suite still works. On a 700-object board this took the output from 872KB to 1,022KB of vector.
Also, the CLI printed errors but exited 0, so failures were invisible to scripts. It now exits non-zero.
MIRO_TOKENand.envThe token can come from the environment rather than
--tokenon every invocation. Uses Node's ownprocess.loadEnvFile, so no new dependency. Precedence is explicit token → real environment →.env..envis git-ignored and.env.exampledocuments the variables, including the ones the existing integration tests need.list-boardsLists the boards a token can reach, via the same endpoint the Miro dashboard uses. No browser, so it returns in about a second for ~190 boards. Supports
table/json/idsoutput and a title filter.One thing worth recording: Miro's offset paging on this endpoint is unreliable. Following
nextLinkfromlimit=25returned 26 of 189 boards and then stopped, andnextLinkdisappears entirely for larger limits. So the whole list is requested in one page by default,nextLinkis still followed when present, and results are de-duplicated by ID.draw.io export (
-e drawio, and an offlineconvertcommand)Converts board objects to native mxGraph — real vertices, edges and containers, not an embedded image. Frames become containers with their children nested, sticky notes and shapes keep their fill/border/font, and connectors become edges with real
source/target, arrow heads, captions and exit/entry anchors.convertworks from a saved JSON export without contacting Miro, which matters when converting many boards.Three things Miro doesn't record, reconstructed and documented in the README:
zOrder: "source"opts out).fontSizefor sticky notes — it fits text to the note. Left alone, every sticky falls back to draw.io's 12pt default and a whole board reads as one size. Sizes are derived by wrapping at spaces and fitting to the shape; because text only breaks at spaces, the longest word sets the ceiling. Shapes and text keep the size Miro recorded.curved=1draws a straight line. Curved connectors get an orthogonal route to smooth.Images become labelled placeholders — the JSON export carries no image data or usable URL (all 37 on my test board have
url: ""). SVG export remains the only format that preserves image content.backupWalks the whole board list into a timestamped directory, one directory per board, with a
manifest.jsonrecording status, attempts, paths, sizes and durations.Serial with a configurable pause between boards, since the cost is per-board browser work rather than per API call. Each board is retried with a doubling backoff capped at five minutes; a board that exhausts its retries is recorded and the run continues rather than aborting.
--resume <dir>continues an interrupted run, skipping boards that already have all their files.--dry-runshows the plan without contacting Miro.Board titles are sanitised into directory names: slashes, colons and the characters Windows reserves are replaced, control characters stripped, trailing dots/spaces removed, names Windows reserves outright (
CON,NUL,COM1…) suffixed, long titles shortened. The board ID is appended so boards sharing a title never collide.Tests
58 new unit tests, all offline and network-free, covering token resolution, board listing (stubbed
fetch), the draw.io conversion, and the backup helpers. The new live test skips itself whenMIRO_TOKENisn't set.pnpm run lintandpnpm run buildpass.One note on CI:
test:board-object-typesandtest:apiexit 1 whenTEST_BOARD_IDand friends are unset, so theon: pushworkflow fails on a fork, which has no access to the secrets. That's pre-existing behaviour and I left it alone rather than change a decision that looked deliberate — but I'm happy to make them skip instead if you'd like fork CI to be green.🤖 Generated with Claude Code