Skip to content

Local Storage and JSON Import Export

Filip Horvat edited this page May 22, 2026 · 2 revisions

Local Project Storage + JSON Import / Export

The planner is server-less β€” no account, no cloud, no backend. Every project lives entirely in your browser's IndexedDB database, and you can export it as a portable JSON file at any time.

Feature line from the README: πŸ’Ύ Local project storage + JSON import/export.

Where projects live

The browser opens a single IndexedDB database (mlp.db) with three object stores:

Store Purpose
projects One record per project (ProjectRecord) keyed by project id.
thumbnails An optional thumbnail blob per project (rendered on the dashboard).
blobs Large binary payloads such as background images, keyed bg:<projectId>:<imageId>.

The repository wraps the database with a friendly typed API (ProjectRepository):

  • listProjects() β†’ list of ProjectSummary (id, name, surface count, material count, thumbnail).
  • getProject(id) β†’ migrated Project or null.
  • saveProject(project) β†’ validates with the Zod ProjectSchema and persists.
  • deleteProject(id) β†’ removes the project, its thumbnail and every bg:<id>: blob in a single transaction.
  • duplicateProject(id, newName) β†’ deep clones a project with a fresh id and timestamps.
  • putBlob / getBlob / deleteBlob / putThumbnail / getThumbnail.

Saving

You don't have to think about saving:

  • Autosave β€” a debounced subscription on the project store calls repo.saveProject(...) every ~500 ms after the last edit. A toast is shown on failure; the editor toolbar displays Saved HH:MM:SS or Not saved plus an unsaved-dot indicator.
  • Manual save β€” Ctrl+S or Save now in the File menu triggers an immediate save through useSaveProject. The repo writes the same ProjectRecord, so manual and automatic saves are interchangeable.
  • Schema validation β€” every save runs through ProjectSchema.parse. An invalid project (e.g. produced by a future bug) is rejected before it can corrupt your database.

Migrations

migrateProject (under src/storage/migrations.ts) brings older project JSON or older IndexedDB records up to the current schemaVersion. This runs:

  • On getProject(id) after reading from IndexedDB.
  • On parseProjectFromJson(text) when importing.

That means you can open an old export, or move your data to a newer build of the app, and it just works.

JSON export

From the File menu or Dashboard:

  1. downloadProjectJson(project) calls exportProjectToJson(project) which serialises the whole Project with JSON.stringify(project, null, 2).
  2. The download is named <slugified-name>-<last 6 of id>.mlp.json (for example kitchen-floor-3f4a91.mlp.json).
  3. A toast confirms the export, or surfaces the error message if the browser blocks the download.

Exports are plain JSON β€” they can be diffed, committed to git, or embedded in a backup workflow.

JSON import

Two import surfaces:

  • The dashboard top bar keeps the shared theme button behaviour: New project uses the accent hover state, while Import JSON keeps the neutral surface hover treatment.
  • Dashboard β†’ Import JSON β€” pops a file picker, parses, saves into IndexedDB and opens the new project.
  • File menu β†’ Import project JSON… β€” same workflow, but from inside an open project. The replacement is loaded into the editor and the hash route is updated.

Both routes use parseProjectFromJson:

  1. JSON.parse(text) β€” raises ProjectImportError with a clear message on invalid JSON.
  2. migrateProject(...) β€” applies migrations and Zod validation. If the file is malformed or from a future incompatible schema, a ProjectImportError describes exactly what went wrong.

Toasts on success / failure mirror the export workflow.

Where this lives in the codebase

  • IndexedDB wrapper β€” src/storage/indexedDb.ts
  • Repository β€” src/storage/projectRepository.ts
  • Autosave β€” src/storage/autosave.ts
  • JSON import / export β€” src/storage/jsonImportExport.ts
  • Migrations β€” src/storage/migrations.ts
  • Manual save hook β€” src/features/editor/useSaveProject.ts
  • Dashboard β€” src/features/dashboard/DashboardPage.tsx

Related pages

Clone this wiki locally