From 41d198f6fd910bc4b0dc0643949fcbb87a4a023c Mon Sep 17 00:00:00 2001 From: engmung <122682380+engmung@users.noreply.github.com> Date: Sat, 15 Aug 2026 21:23:58 +0900 Subject: [PATCH] fix(web): the .pfs is the performance, so stop shipping a JSON nothing opens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Upstream made Open/Save in the Director the same .pfs table the panel plays (SimonePDA 9469220), and dropped the JSON demos with it. Our design assumed the split it replaced — JSON as the editable source, .pfs as the build output — so a deck pack carried both and the UI told people the JSON was the file to keep working on. That is now simply untrue, and a file nothing opens is weight in someone's download. Packs carry the table alone. The pattern page's second link is relabelled "raw" and described as what it is: the same cues for reading or scripting, not something the Director will open. Uploads still accept .json, because Directors from before the format settled saved it. The stored form stays JSON — it is the site's own canonical shape, what summaries and validation read and what regenerates the table on the way out, and it never had a life outside this server. The comments that called it "the editable source" now say so. Co-Authored-By: Claude Fable 5 --- web/scripts/deck-pack-smoke.ts | 11 ++---- web/src/app/api/community/decks/[id]/route.ts | 6 +-- .../app/community/d/[id]/DeckDetailClient.tsx | 18 ++++----- .../community/p/[id]/PatternDetailClient.tsx | 6 +-- .../community/AddPerformanceModal.tsx | 18 +++++---- web/src/lib/community/deckZip.ts | 11 ++++-- web/src/lib/community/performance.ts | 37 +++++++++++-------- web/src/lib/community/performanceFile.ts | 10 ++--- 8 files changed, 63 insertions(+), 54 deletions(-) diff --git a/web/scripts/deck-pack-smoke.ts b/web/scripts/deck-pack-smoke.ts index a03f9a1..8184b62 100644 --- a/web/scripts/deck-pack-smoke.ts +++ b/web/scripts/deck-pack-smoke.ts @@ -15,7 +15,7 @@ * * The PERFORMANCE decoration runs on every download of a deck that has one, * and it rewrites the archive — so a mistake there breaks the pack itself, - * not just the performance. It must add both forms, leave the modules alone, + * not just the performance. It must add the table, leave the modules alone, * and refuse to fail loudly: a broken attachment may cost its own file and * nothing else. */ @@ -146,13 +146,10 @@ const names = Object.keys(decorated).sort(); check("the modules survive untouched", names.includes("wave_saw.pfm"), true); check("so does the running order", names.includes("catalog.txt"), true); -check("the editable source is added", names.includes("performance.json"), true); check("the packed table is added, named from the id", names.includes("sunset_set.pfs"), true); -check( - "performance.json is stored verbatim", - new TextDecoder().decode(decorated["performance.json"]), - performanceJson, -); +// The Director opens and saves .pfs, so the table IS the document — a JSON +// beside it would be a file nothing opens. +check("no JSON rides along", names.includes("performance.json"), false); // The .pfs is what the panel plays, so it has to be a table, not bytes that // merely exist — decode it back and check the timeline survived the trip. diff --git a/web/src/app/api/community/decks/[id]/route.ts b/web/src/app/api/community/decks/[id]/route.ts index d2fb1bd..bdb7859 100644 --- a/web/src/app/api/community/decks/[id]/route.ts +++ b/web/src/app/api/community/decks/[id]/route.ts @@ -138,9 +138,9 @@ async function handlePatch(request: Request, context: { params: Promise<{ id: st // Optional attached performance (Director timeline). null detaches; a // string is validated against the same limits the device's .pfs player - // enforces, and stored in canonical form so the pack's performance.json - // is stable across re-serializations. Serve-time zip decoration means no - // rebuild is queued either way. + // enforces, and stored canonically so the table the pack ships is stable + // across re-serializations. Serve-time zip decoration means no rebuild is + // queued either way. let performanceJson: string | null | undefined; if (raw.performanceJson !== undefined) { if (raw.performanceJson === null || raw.performanceJson === "") { diff --git a/web/src/app/community/d/[id]/DeckDetailClient.tsx b/web/src/app/community/d/[id]/DeckDetailClient.tsx index fb6e6f3..affd0c4 100644 --- a/web/src/app/community/d/[id]/DeckDetailClient.tsx +++ b/web/src/app/community/d/[id]/DeckDetailClient.tsx @@ -438,7 +438,7 @@ export default function DeckDetailClient({