Build SmartDraw
KINDAback to the verdict
A local diagram editor whose hard problems are an edge that references a shape rather than a pair of coordinates, an undo stack that covers every operation including the ones autosave performed, and an export that is self-contained rather than dependent on the machine that opens it.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the development server and the build.
Get it nodejs.org or your version manager. open ↗
Verify
node --version prints v22 or higher - have readyfree with an open licence
Why Export quality depends on embedding the font, and embedding one whose licence forbids it is a problem you create for every user of your diagrams.
Get it Choose an open licensed font family and keep the licence file with the project.
Verify
The licence permits embedding and the licence file is in the repository - have readyfree
Why Canvas rasterisation, font loading and pointer events are where engines differ, and a diagram editor lives in all three.
Get it Install a second browser with a different engine from your usual one.
Verify
Export and drag behave identically in both - decidefree
Why Changing the format after people have files is the expensive mistake in this category, and a written schema with a version number makes migration a task rather than an emergency.
Get it Write the schema with its version field, and the rule for when the version increments.
Verify
The schema document exists and the code reads its version on open
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- documents(id, title, schema_version, created_at, updated_at, canvas_w, canvas_h) - nodes(id, doc_id, kind, x, y, w, h, rotation, text, style_json) - ports(node_id, name, rel_x, rel_y) · anchors expressed as fractions of the shape box so resizing moves them correctly - edges(id, doc_id, from_node, from_port, to_node, to_port, route_mode, waypoints_json, style_json) · endpoints are references, never coordinates - commands(doc_id, ordinal, label, forward_json, inverse_json, at) · the undo stack, persisted so a reload does not erase it - assets(id, doc_id, kind, mime, bytes, sha256) · fonts and images stored in the document so an export is self-contained - snapshots(doc_id, at, blob) · a periodic whole-document copy, because a command log with one corrupt entry is a lost diagram
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
VITE_APP_TITLE | required | Diagrams | Shown in the tab and in exported file names. |
VITE_SNAP_DISTANCE_PX | required | 8 | Snapping distance. People disagree about this, so it is configuration. |
VITE_HISTORY_MEMORY_BUDGET_MB | required | 24 | The undo stack is trimmed rather than allowed to grow without limit. |
VITE_AUTOSNAPSHOT_SECONDS | required | 120 | How often a whole-document snapshot is written for recovery. |
VITE_SCHEMA_VERSION | required | 3 | Written into every document and checked on open. |
The build, in order
An edge that points at a shape
Moving, resizing or deleting a shape does the right thing to everything attached to it.
Absolute offsets break on the first resize.
Files
src/model/ports.tstests/geometry.test.tsThe stored document should be the truth, not a cache of pixels.
Files
src/model/edges.tsBoth answers are fine; silently leaving arrows to nowhere is not.
Files
src/model/document.ts
done when · tick each as it passeswatch out- Storing an edge as two points because it renders faster means every arrow detaches as soon as a box is dragged, and the user's repair work is what they will remember about your editor. Store references and ports, compute the points on render, and test by moving a shape a thousand pixels and asserting the edge still touches it.
Undo that covers everything
Every change is one entry on a stack that survives a reload, including changes made while you were not looking.
Snapshots are for recovery; a history of them is a memory leak.
Files
src/history/commands.tstests/history.test.tsTwelve undos for one drag is the same as no undo.
Files
src/history/transaction.tsA reload should not be a wall you cannot step back through.
Files
src/history/persist.ts
done when · tick each as it passeswatch out- Implementing undo as a stack of whole-document snapshots works until a diagram has a few hundred objects and an embedded image, at which point every drag allocates megabytes and the editor stutters exactly when the user is being precise. Store patches, keep periodic snapshots for recovery only, and measure the memory the stack uses after two hundred operations.
A canvas that does not fight you
Selecting, dragging, snapping and typing feel right at the size of diagram people actually draw.
Bounding boxes are why cheap editors feel cheap.
Files
src/canvas/hit.tstests/hit.test.tsSnapping you cannot switch off is a trap.
Files
src/canvas/snap.tsSeveral hundred objects is a normal diagram, not a stress test.
Files
src/canvas/render.ts
done when · tick each as it passeswatch out- Hit testing on bounding boxes makes every diamond and circle grab clicks from the shape behind it, and the user cannot select the thing under the corner of a rotated box no matter how carefully they aim. Test against the actual geometry, and verify with a rotated shape overlapping another.
Routing that goes around things
Connectors take a sensible path and keep any path the user set by hand.
A general path finder is overkill; obstacle corners are enough.
Files
src/routing/orthogonal.tstests/routing.test.tsThe user moved it because your router was wrong.
Files
src/routing/manual.tsRouting during a drag is how editors get a reputation.
Files
src/routing/scheduler.ts
done when · tick each as it passeswatch out- Recomputing every route on every frame of a drag turns a smooth gesture into a slideshow on a medium diagram, and recomputing all of them on load makes opening a document feel broken. Recompute only affected edges, and do it after the drag settles rather than during it.
An export that stands on its ownproduct builder
The exported file looks the same on a machine that has none of your fonts, and the document format is portable.
An external reference is what makes the canvas refuse to give you the image.
Files
src/export/inline.tstests/export.test.tsA whole font is megabytes and a diagram uses forty glyphs.
Files
src/export/fonts.tsThe second version is the one that loses people's files.
Files
src/model/migrate.ts
done when · tick each as it passeswatch out- Rasterising an SVG that still references an image or a font by URL taints the canvas, and the browser then throws a security error from the very call you use to produce the PNG, so your export button fails on exactly the diagrams that have pictures in them. Inline every external resource as a data URI first, and test the PNG path with a document containing an embedded image.
That is the whole plan for SmartDraw. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Real time collaboration.
- Importing other vendors' formats.
- A large template and stencil library.
- Automatic whole-diagram layout.
- large template libraries
- high-fidelity import and export
- presentation and enterprise controls
- multiplayer cursors and comments
- A read-only viewer link that renders an exported document, which is most of what sharing actually means
- Layers, which change selection and hit testing enough to be worth doing after the basics are solid
- A text-to-diagram importer for one simple syntax, which is a better use of a model than generating shapes freehand
Need the files? The project pack on the verdict page hands your agent the whole brief · more diagrams.