Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

LocalStorage persistence

How codeplug projects are saved and restored in the browser.

Tracking: codeplug-tool#9

Problem

Codeplug state should survive page reloads without re-importing. Data stays in the browser only — never in the repository.

What is persisted

A versioned projects envelope at a single LocalStorage key:

{
  "version": 1,
  "activeProjectId": "",
  "projects": [
    {
      "id": "",
      "name": "Channels",
      "createdAt": "2026-06-18T…",
      "updatedAt": "2026-06-18T…",
      "codeplug": { "channels": [], "zones": [], "…": "" }
    }
  ]
}

Each codeplug object is a full Codeplug (channels, zones, stubs, meta).

Storage keys

Key Purpose
mm9pdy-codeplug-tool.codeplug Projects envelope (CODEPLUG_STORAGE_KEY)
mm9pdy-codeplug-tool.channel-map.mapboxToken Mapbox token — separate, map-only
mm9pdy-codeplug-tool.channel-map.tileProvider Tile provider preference — separate
mm9pdy-codeplug-tool.list.*.{projectId} Entity list filters/sort prefs — see UI list table state

Legacy (migrated on read): channels-list-columns, channels-list-columns-schema.

Versioning

Constant Value Gates
CODEPLUG_STORAGE_VERSION 1 On-disk envelope shape
CODEPLUG_SCHEMA_VERSION 17 Inner Codeplug model shape (meta.schemaVersion); older codeplugs migrate on load

Unknown or future envelope version → boot with an empty project set (no crash).

Schema v7 migration (v6 → v7) — one-time uplift

On the first load of a persisted v6 codeplug, wire names are resolved to id FKs once; subsequent loads at v7+ trust persisted model fields and do not re-resolve from provenance:

  • Channel.contactNamecontactRef (EntityRef | null); wire string preserved in meta.imported.contactWireName
  • Channel.rxGroupListNamerxGroupListId; wire string in meta.imported.rxGroupListWireName
  • RxGroupList provenance memberWireNamesmemberRefs when talk groups/contacts are available and memberRefs is still empty
  • Dangling legacy wire names become null refs (not errors)

At schema v7 and above, migrateCodeplug normalises shape only — operator edits to contactRef, rxGroupListId, and memberRefs survive reload. See import-export fidelity contract — Provenance is not re-applied on load.

Code: migrateCodeplug · fixture in codeplugStorage.test.ts.

Load / save / clear

Action LocalStorage
Import on home (new project) Save envelope
Import on Import & export (active project) Save
Switch active project Save
Delete project Save (or remove key if last project)
Empty active codeplug (project kept) Save
Empty project set Key removed
Corrupt JSON on load Key removed; boot empty
Partially invalid projects Invalid entries filtered; activeProjectId fixed up

Code: src/state/codeplugStorage.ts, wired from src/state/codeplugStore.tsx.

Quota handling

saveProjectsToStorage throws StorageQuotaError when setItem hits QuotaExceededError. The store surfaces a dismissible yellow Alert in import UI; in-memory state is not blocked — the user can keep working but may lose data on reload.

Size limits

  • Typical browser limit: ~5 MB per origin (varies by browser and other keys on the same origin).
  • A medium codeplug (hundreds of channels, dozens of zones) is usually well under 1 MB as JSON.
  • Multiple projects share the same quota. Very large codeplugs or many projects may need #32 (IndexedDB / OPFS).

Cloud interchange (not the edit store)

Google Drive (#17) provides explicit open/save of native YAML and CPS files. The working edit store remains this LocalStorage envelope — cloud is an optional interchange layer alongside file download. See cloud-storage.

Privacy

  • All project data is browser-local only.
  • Never commit operator CSV exports or LocalStorage dumps to the repo.
  • Use sample-exports/ (gitignored) for local testing.

Manual verify

  1. npm run dev → import a codeplug on the home page → open /channels.
  2. Hard refresh (Cmd+R) — projects and active selection restored.
  3. Import a second codeplug from home — both listed after refresh.
  4. Delete a project — confirm dialog; refresh — deletion persists.
  5. DevTools → Application → Local Storage — confirm mm9pdy-codeplug-tool.codeplug envelope.

Related