Build SmartDraw

KINDAback to the verdict

0%0 of 16 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

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.

estimated effort multi-daythe files for this build are in the project pack

RuntimeReact with TypeScript on Vite, no server, nothing leaves the browserStorageIndexedDB holding a versioned document format plus periodic snapshotsGeometryEdges reference shape identifiers and named ports, never absolute pointsHistoryA command log of forward and inverse patches, grouped per user gestureExportSVG with every font and image embedded, and PNG rasterised from that SVG

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. 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

  2. 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

  3. 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

  4. 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.

VariableNeededExampleWhere the value comes from
VITE_APP_TITLErequiredDiagramsShown in the tab and in exported file names.
VITE_SNAP_DISTANCE_PXrequired8Snapping distance. People disagree about this, so it is configuration.
VITE_HISTORY_MEMORY_BUDGET_MBrequired24The undo stack is trimmed rather than allowed to grow without limit.
VITE_AUTOSNAPSHOT_SECONDSrequired120How often a whole-document snapshot is written for recovery.
VITE_SCHEMA_VERSIONrequired3Written into every document and checked on open.

The build, in order

  1. An edge that points at a shape

    Moving, resizing or deleting a shape does the right thing to everything attached to it.

    1. Absolute offsets break on the first resize.

      Files src/model/ports.tstests/geometry.test.ts

    2. The stored document should be the truth, not a cache of pixels.

      Files src/model/edges.ts

    3. Both answers are fine; silently leaving arrows to nowhere is not.

      Files src/model/document.ts

    done when · tick each as it passes
    watch 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.
  2. Undo that covers everything

    Every change is one entry on a stack that survives a reload, including changes made while you were not looking.

    1. Snapshots are for recovery; a history of them is a memory leak.

      Files src/history/commands.tstests/history.test.ts

    2. Twelve undos for one drag is the same as no undo.

      Files src/history/transaction.ts

    3. A reload should not be a wall you cannot step back through.

      Files src/history/persist.ts

    done when · tick each as it passes
    watch 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.
  3. A canvas that does not fight you

    Selecting, dragging, snapping and typing feel right at the size of diagram people actually draw.

    1. Bounding boxes are why cheap editors feel cheap.

      Files src/canvas/hit.tstests/hit.test.ts

    2. Snapping you cannot switch off is a trap.

      Files src/canvas/snap.ts

    3. Several hundred objects is a normal diagram, not a stress test.

      Files src/canvas/render.ts

    done when · tick each as it passes
    watch 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.
  4. Routing that goes around things

    Connectors take a sensible path and keep any path the user set by hand.

    1. A general path finder is overkill; obstacle corners are enough.

      Files src/routing/orthogonal.tstests/routing.test.ts

    2. The user moved it because your router was wrong.

      Files src/routing/manual.ts

    3. Routing during a drag is how editors get a reputation.

      Files src/routing/scheduler.ts

    done when · tick each as it passes
    watch 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.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more diagrams.