Build SimpleMind

YESback 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 mind map editor whose hard problems are refusing to treat evictable browser storage as an archive, keeping the layout identical every time the same file is opened, and exporting a picture that still contains its fonts and images once the browser rasterises it.

estimated effort weekendthe files for this build are in the project pack

RuntimeReact with Vite, no backend at all, served from a static host or opened locallyWorking storeIndexedDB with persistence requested and the granted answer displayedArchiveOne JSON file per map, written back through the file handle you openedLayoutA deterministic tidy tree computed from the document, with manual positions stored per nodeExportSVG with fonts and images embedded as data, then PNG rasterised from that same 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 Builds the application; nothing runs server-side once it is built.

    Get it nodejs.org or a version manager. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why The archive design depends on writing back to a file the person chose, and the honesty design depends on being able to ask whether storage is persistent.

    Get it A current desktop browser; check the support tables for both APIs before committing.

    Verify Opening a file, editing and saving writes back to the same file without a download

  3. have readyfree with an open licence

    Why The export embeds a subset of the font in the SVG, which is a use the font's licence either allows or does not, and finding out afterwards is the wrong order.

    Get it Choose a font whose licence permits embedding and keep the licence file with the project.

    Verify The licence text permits embedding and the file is in the repository

  4. have readyfree

    Why Layout determinism and layout speed are both invisible on a map with twelve nodes, and both are things people abandon an editor over.

    Get it Generate one, or import a large outline.

    Verify Layout time is measured and recorded and two layouts are identical

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- document(format_version, id, title, created_at, updated_at, root_id) · the envelope, versioned from the first release
- nodes(id, parent_id, ordinal, text, note, collapsed, style_id, manual_x, manual_y) · manual_x and manual_y stay null until somebody drags that node
- links(id, from_node, to_node, kind, label) · the cross connections that make this a map rather than an outline
- styles(id, name, fill, stroke, text_colour, font_family, font_size) · referenced by id so restyling a branch is one write
- assets(id, kind, mime, bytes_base64, sha256) · images live inside the document, because an image referenced by path is an image that goes missing
- snapshots(id, at, sha256, bytes) · the last few versions that opened cleanly, kept separately from the working copy
- The whole document is one JSON object that round-trips byte-identically through save and load. If it does not, you have a serialisation format rather than a file format, and the difference shows up the first time you diff two versions.

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
APP_TITLErequiredMapsShown in the tab and in the export metadata. No secrets exist in this build.
SNAPSHOT_KEEPrequired5How many clean snapshots to retain for recovery. They are small.
AUTOSAVE_IDLE_MSrequired800Idle time before the working copy is written and undo entries are grouped.
EXPORT_PNG_SCALErequired2Rasterisation scale for PNG export. Two is the sensible default.
MAX_ASSET_BYTESrequired2000000Largest image that can be embedded in a document, since the file carries it.

The build, in order

  1. A document that is a file

    Saving means a file on disk, and the interface says what the browser promised about the working copy.

    1. The interesting case is the browser saying no.

      Files src/storage/persist.tstests/persist.test.ts

    2. Falling back to a download where the browser has no handle.

      Files src/storage/file.ts

    3. That is the only meaning that protects anybody.

      Files src/ui/DirtyDot.tsx

    done when · tick each as it passes
    watch out
    • Autosaving to IndexedDB and showing the word 'saved' is a promise the browser has not made, because the storage standard starts every bucket in best-effort mode and says an agent under storage pressure should clear best-effort buckets, so the map can vanish between sessions with no error anywhere; request persistence, display what the browser actually granted, and reserve the word 'saved' for bytes that reached a file.
  2. Editing at the speed of thinking

    A map can be built entirely from the keyboard and undo removes what a person would call one action.

    1. One action to a person is one entry in the stack.

      Files src/history/group.tstests/undo-grouping.test.ts

    2. This is a thinking tool, and the mouse is the slow path.

      Files src/keys.ts

    3. Restoring the parent but not the position is a half undo.

      Files src/history/inverse.ts

    done when · tick each as it passes
    watch out
    • One undo entry per keystroke means undo after typing a node title deletes a single letter, so the person hits it eleven times, overshoots into the previous action and loses a branch they wanted; group consecutive edits to the same node within an idle window into one entry, make every structural change its own entry, and test that undo after a reparent restores both the tree and the layout rather than only one of them.
  3. A layout that holds still

    Opening the same file twice produces pixel-identical geometry.

    1. No clock, no random, no map iteration order.

      Files src/layout/tidy.tstests/layout-determinism.test.ts

    2. The whole point of dragging a node is that it stays dragged.

      Files src/layout/pins.ts

    3. Five hundred nodes is a real map, not a stress test.

      Files tests/layout-perf.test.tsREADME.md

    done when · tick each as it passes
    watch out
    • A layout that iterates over a hash map, uses any randomness or depends on insertion order gives different coordinates for the same file on different runs, and because the difference is small people notice it as the map feeling unstable rather than as a bug they can report; sort children by explicit ordinal, make the layout a pure function with no clock and no random source, and assert in a test that laying out the same document twice produces identical numbers.
  4. Export that still contains its text

    The exported SVG and PNG look like the screen, including fonts and embedded images.

    1. External references are not fetched when the SVG is an image.

      Files src/export/svg.tstests/export-font.test.ts

    2. Two renderers is two sets of differences.

      Files src/export/png.ts

    3. Text width is the measurement that catches a missing font.

      Files tests/export-parity.test.ts

    done when · tick each as it passes
    watch out
    • An SVG placed in an image element is processed in one of the secure modes defined by the SVG conformance rules, which forbid resolving external references, so your linked web font and your linked images simply do not render and the PNG comes out in a fallback face with holes where the pictures were, while the same SVG opened directly in a tab looks perfect; embed the font subset and every image as data inside the SVG, and compare the exported PNG against the screen rather than trusting that it worked.
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.