feat(npcs): drop binding; select NPCs on storyline export

Remove storyline/scene NPC binding and export chosen NPCs with relations/groups. Harden export modal so a missing npcs payload no longer blacks out the editor.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-30 10:05:09 +08:00
parent 04c75cd725
commit e08f5ef550
17 changed files with 418 additions and 522 deletions
+5 -7
View File
@@ -757,7 +757,7 @@ async function main() {
});
registerHandler(
ipcChannels.project.upsertNpc,
async ({ npcId, name, description, filePath: pathFromDrop, groupId, binding }) => {
async ({ npcId, name, description, filePath: pathFromDrop, groupId }) => {
let filePath = pathFromDrop;
if (!filePath && !npcId) {
const { canceled, filePaths } = await dialog.showOpenDialog({
@@ -781,7 +781,6 @@ async function main() {
...(typeof description === 'string' ? { description } : {}),
...(filePath ? { filePath } : {}),
...(groupId !== undefined ? { groupId } : {}),
...(binding !== undefined ? { binding } : {}),
},
(p) => emitNpcUpsertProgress(p),
);
@@ -793,12 +792,11 @@ async function main() {
);
registerHandler(
ipcChannels.project.updateNpcFields,
async ({ npcId, name, description, groupId, binding }) => {
async ({ npcId, name, description, groupId }) => {
const project = await projectStore.updateNpcFields(npcId, {
...(typeof name === 'string' ? { name } : {}),
...(typeof description === 'string' ? { description } : {}),
...(groupId !== undefined ? { groupId } : {}),
...(binding !== undefined ? { binding } : {}),
});
emitSessionState();
return { project };
@@ -1017,8 +1015,7 @@ async function main() {
return { canceled: false as const, project };
});
registerHandler(ipcChannels.project.getProjectStorylines, async ({ projectId, labels }) => {
const storylines = await projectStore.getProjectStorylines(projectId, labels);
return { storylines };
return projectStore.getProjectStorylines(projectId, labels);
});
registerHandler(ipcChannels.project.peekImportZip, async ({ labels, targetHasMainStart }) => {
const { canceled, filePaths } = await dialog.showOpenDialog({
@@ -1159,7 +1156,7 @@ async function main() {
throw e;
}
});
registerHandler(ipcChannels.project.exportZip, async ({ projectId, storylineSelections, labels }) => {
registerHandler(ipcChannels.project.exportZip, async ({ projectId, storylineSelections, npcIds, labels }) => {
const list = await projectStore.listProjects();
const entry = list.find((p) => p.id === projectId);
if (!entry) {
@@ -1183,6 +1180,7 @@ async function main() {
await projectStore.exportStorylinesZipToPath(
projectId,
storylineSelections,
npcIds ?? [],
dest,
labels,
(p) => {