From 288ab4652175fd1f95db1cbd8d51ec321da7b60e Mon Sep 17 00:00:00 2001 From: David Montero Date: Sun, 7 Jun 2026 00:47:39 +0200 Subject: [PATCH] feat(frontend): persist + restore project library manifest (P2.4 projects) Saved projects now round-trip their declared library manifest (compile scope): buildSavePayload includes libraries_json from useLibraryManifestStore; loading a project restores it (and clears any stale example manifest). Existing projects load with an empty manifest -> legacy scan-all (unchanged); new saves capture whatever manifest is active. Pairs with the backend libraries_json column. --- frontend/src/pages/ProjectByIdPage.tsx | 9 +++++++++ frontend/src/services/projectService.ts | 2 ++ frontend/src/utils/projectPayload.ts | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/frontend/src/pages/ProjectByIdPage.tsx b/frontend/src/pages/ProjectByIdPage.tsx index 90a5f188..2c9b311e 100644 --- a/frontend/src/pages/ProjectByIdPage.tsx +++ b/frontend/src/pages/ProjectByIdPage.tsx @@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom'; import { getProjectById } from '../services/projectService'; import { useSimulatorStore } from '../store/useSimulatorStore'; import { useProjectStore } from '../store/useProjectStore'; +import { useLibraryManifestStore } from '../store/useLibraryManifestStore'; import { useSEO } from '../utils/useSEO'; import { EditorPage } from './EditorPage'; import type { BoardInstance, BoardKind } from '../types/board'; @@ -56,6 +57,14 @@ export const ProjectByIdPage: React.FC = () => { .then((project) => { const payload = buildLoadPayload(project); loadProjectState(payload); + // P2.4 — restore the declared library manifest (compile scope), also + // clearing any stale example manifest from before this load. + try { + const libs = JSON.parse(project.libraries_json || '[]'); + useLibraryManifestStore.getState().setLibraries(Array.isArray(libs) ? libs : null); + } catch { + useLibraryManifestStore.getState().setLibraries(null); + } setCurrentProject({ id: project.id, slug: project.slug, diff --git a/frontend/src/services/projectService.ts b/frontend/src/services/projectService.ts index e9519545..f2fd33a8 100644 --- a/frontend/src/services/projectService.ts +++ b/frontend/src/services/projectService.ts @@ -42,6 +42,7 @@ export interface ProjectResponse { components_json: string; wires_json: string; boards_json: string; // serialized BoardInstance[] + libraries_json?: string; // P2.4 — declared library manifest (compile scope) owner_username: string; created_at: string; updated_at: string; @@ -62,6 +63,7 @@ export interface ProjectSaveData { components_json: string; wires_json: string; boards_json?: string; // serialized BoardInstance[] + libraries_json?: string; // P2.4 — declared library manifest (compile scope) } export async function getMyProjects(): Promise { diff --git a/frontend/src/utils/projectPayload.ts b/frontend/src/utils/projectPayload.ts index e253fc7a..674a930c 100644 --- a/frontend/src/utils/projectPayload.ts +++ b/frontend/src/utils/projectPayload.ts @@ -11,6 +11,7 @@ import type { BoardInstance } from '../types/board'; import type { Wire } from '../types/wire'; import { useEditorStore, chipFileGroupId } from '../store/useEditorStore'; import { useSimulatorStore } from '../store/useSimulatorStore'; +import { useLibraryManifestStore } from '../store/useLibraryManifestStore'; /** * Editor groups owned by programmable custom-chips on the canvas (those whose @@ -107,6 +108,9 @@ export function buildSavePayload(meta: SnapshotInputs = {}): ProjectSaveData { components_json: JSON.stringify(sim.components), wires_json: JSON.stringify(sim.wires), boards_json: JSON.stringify(sim.boards.map(serialisableBoard)), + // P2.4 — persist the declared library manifest (compile scope) so reloading + // the project re-sends it. Empty when the project declares no libraries. + libraries_json: JSON.stringify(useLibraryManifestStore.getState().libraries ?? []), }; }