Keep creation as a living DAG and archive playable products separately.

Opening accept no longer locks the graph; session.json stays creation-only while play starts from an instance snapshot. Sidebar products nest run saves, and play reading fills the feed at a denser type size.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-01 00:52:05 +08:00
co-authored by Cursor
parent bd5d88abfa
commit f37388761b
22 changed files with 930 additions and 303 deletions
+38 -2
View File
@@ -395,7 +395,17 @@ export async function handleBooksApi(
return true;
}
try {
const session = await sessionManager.startNewPlayRun(active.id);
let instanceId: string | undefined;
try {
const raw = await readBody(req);
if (raw.trim()) {
const body = JSON.parse(raw) as { instanceId?: string };
instanceId = body.instanceId?.trim() || undefined;
}
} catch {
instanceId = undefined;
}
const session = await sessionManager.startNewPlayRun(active.id, instanceId);
json(res, 200, { session });
} catch (err) {
json(res, 400, {
@@ -420,7 +430,11 @@ export async function handleBooksApi(
...s,
kindLabel: s.kind === "instance" ? "创作定稿" : "游玩进度",
}));
json(res, 200, { saves });
json(res, 200, {
saves,
playWorkingInstanceId:
sessionManager.playWorkingInstanceId(bookId) ?? null,
});
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "读取存档失败",
@@ -490,6 +504,28 @@ export async function handleBooksApi(
return true;
}
if ((req.method === "PATCH" || req.method === "PUT") && !isLoad) {
const body = JSON.parse(await readBody(req)) as { label?: string };
try {
const save = sessionManager.renameGameSnapshot(
bookId,
saveId,
body.label ?? "",
);
json(res, 200, {
save: {
...save,
kindLabel: save.kind === "instance" ? "创作定稿" : "游玩进度",
},
});
} catch (err) {
json(res, 400, {
error: err instanceof Error ? err.message : "重命名失败",
});
}
return true;
}
if (req.method === "DELETE") {
try {
sessionManager.deleteGameSnapshot(bookId, saveId);