Contributor reference for the codeplug project wrapper — the named, persistent, switchable container that holds one codeplug and is the unit users work with across tools.
Tracking: codeplug-tool#9 (persistence + nascent CRUD), codeplug-tool#31 (UI polish), codeplug-tool#76 (nav + route guards), codeplug-tool#102 (start fresh)
Operators often maintain several CPS layouts side by side — e.g. home repeaters, contest weekend, travel — and need to switch between them without re-importing each time. The codeplug (channels, zones, contacts, etc.) is the radio configuration content; the codeplug project is the app-level wrapper that gives that content a name, identity, and persistence boundary.
| Term | Meaning | In code | In docs | Shown to user |
|---|---|---|---|---|
| Codeplug | Radio configuration content | Codeplug — src/models/codeplug.ts |
data-model/ | "codeplug" |
| Codeplug project | Named container holding one codeplug + identity | CodeplugProject — src/models/codeplugProject.ts |
this folder | usually "codeplug" |
| Active project | Currently selected project; tools read/write its codeplug | activeProjectId |
persistence/ | n/a |
User-facing copy avoids the -project suffix where possible ("Import codeplug", "Switch codeplug"). Use "project" only when the switching action needs disambiguation.
interface CodeplugProject {
id: string;
name: string;
description: string; // short one-liner
notes: string; // free-form plain text
author: string; // operator annotation — not used for import/export/validation
targetRadios: string[]; // indicative radio labels — not used for import/export/validation
createdAt: string; // ISO
updatedAt: string; // ISO
codeplug: Codeplug;
}newProject(name, codeplug?) creates a project. On import, deriveProjectNameFromImportFiles(files, { formatLabel }) suggests a name: folder leaf name when the user picked or dropped a directory; otherwise {formatLabel} YYYY-MM-DD (ISO date — formatLabel comes from the active import adapter's projectNameLabel). Fallback when no suggestion: first recognised filename or "Imported codeplug". Per-format examples: import/export hub.
Identity and operator-facing metadata live on the wrapper so Codeplug stays focused on CPS contents. author and targetRadios are indicative human input only — they must never drive import adapters, export profiles, or validation.
src/state/codeplugStore.tsx backs CodeplugProvider:
| Hook | Role |
|---|---|
useCodeplug() |
Active project's codeplug, applyImport, clear, persistence warnings |
useProjects() |
Full list, active id, importNewProject, commitNewProject, setActiveProject, deleteProject, updateProject |
| Action | Effect |
|---|---|
importNewProject |
New project from import; becomes active |
commitNewProject |
New blank project from metadata form; becomes active; first persist on submit (#102) |
applyImport / applyImportToActive |
Merge into active project (or create if none) |
setActiveProject |
Switch active |
deleteProject |
Remove; reassign active |
updateProject |
Patch project metadata (name, description, notes, author, targetRadios) |
clear |
Empty active project's codeplug (project kept) |
Persistence: persistence/README.md — multi-project envelope from storage v1.
| Surface | Behaviour |
|---|---|
Home (/) |
List codeplugs (name + description when set), Start fresh, Import codeplug, Open, Delete (confirm) |
New codeplug (/codeplug/new) |
Public create form — metadata only; Create calls commitNewProject (no persist until submit) (#102) |
Summary (/summary) |
Project dashboard — metadata header, channel map, entity cards (#60, #61) |
Summary edit (/summary/edit) |
Edit project name, description, author, target radios, notes (#60) |
| App nav (always) | Home (when no active project), Reference, Settings |
Export (/export) |
Import & export — vendor format picker; merge/overwrite import + CPS download (#58) |
| Route guards | Project-scoped routes redirect to Home when no active project — RequireActiveProject. /codeplug/new is public (no active project required). |
Start fresh does not write to localStorage until the operator submits Create on /codeplug/new — cancel or navigate away leaves no orphan project.
| Area | Status | Notes |
|---|---|---|
CodeplugProject model |
Shipped | src/models/codeplugProject.ts — includes metadata fields (#60) |
| Multi-project store | Shipped | useCodeplug + useProjects + updateProject |
| LocalStorage persistence | Shipped | persistence/ |
| Landing list / import / delete | Shipped | Home, ProjectList |
| Summary dashboard + edit | Shipped | SummaryDashboard, /summary/edit (#60, #61) |
| Active bar + project nav | Shipped | ActiveProjectBar + project links when a codeplug is active |
| Always-available nav | Shipped | Home, Reference, Settings — #76 |
| Project route guards | Shipped | RequireActiveProject — #76 |
| New empty project | Shipped | Home → /codeplug/new → commitNewProject — #102 |
| Rename via edit screen | Shipped | /summary/edit — partial #31 |
| Duplicate project | Deferred | #31 |
| Active project import | Shipped | Export → ImportIntoActivePanel (#58) |
One codeplug project can be exported to multiple CPS formats without re-importing — see operator lifecycle. Import source format does not lock export targets.
| Doc | Contents |
|---|---|
| operator-lifecycle.md | Create → edit → persist → multi-format export |
| codeplug-project-progress.md | Execution log |
| codeplug-project-outstanding.md | #31 UI debt |
| persistence/README.md | LocalStorage envelope |
| data-model/README.md | Codeplug contents |
Projects are browser-local only. Never commit operator codeplugs or LocalStorage dumps.