feat(tokens): app-local non-player tokens with session moves and UI polish
Add token library/placements, keep play-time moves for the session, lock presentation interactions, and fix export/import modal layout plus freeform trap label. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,6 +23,7 @@ function scene(id: string): Scene {
|
||||
previewRotationDeg: 0,
|
||||
darkenScene: false,
|
||||
traps: [],
|
||||
tokens: [],
|
||||
grid: { enabled: false, type: 'square', sizeN: 0.06, color: '#ffffff' },
|
||||
media: { videos: [], audios: [] },
|
||||
settings: { autoplayVideo: false, autoplayAudio: true, loopVideo: true, loopAudio: true },
|
||||
|
||||
@@ -28,6 +28,7 @@ function scene(id: string, title: string): Scene {
|
||||
previewRotationDeg: 0,
|
||||
darkenScene: false,
|
||||
traps: [],
|
||||
tokens: [],
|
||||
grid: { enabled: false, type: 'square', sizeN: 0.06, color: '#ffffff' },
|
||||
media: { videos: [], audios: [] },
|
||||
settings: { autoplayVideo: false, autoplayAudio: false, loopVideo: false, loopAudio: false },
|
||||
|
||||
@@ -11,7 +11,7 @@ import type {
|
||||
SceneGraphNode,
|
||||
SceneId,
|
||||
} from '../types';
|
||||
import type { AssetId, NpcGroupId, ProjectId } from '../types/ids';
|
||||
import type { AssetId, NpcGroupId, ProjectId, TokenId } from '../types/ids';
|
||||
import {
|
||||
asAssetId,
|
||||
asGraphNodeId,
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
asNpcRelationId,
|
||||
asProjectId,
|
||||
asSceneId,
|
||||
asTokenId,
|
||||
} from '../types/ids';
|
||||
|
||||
import {
|
||||
@@ -812,3 +813,35 @@ function remapNpcBinding(
|
||||
export function newExportBundleProjectId(): ProjectId {
|
||||
return asProjectId(`p_${generateId()}`);
|
||||
}
|
||||
|
||||
/** Id app-токенов, реально стоящих на сценах проекта (для экспорта с линией). */
|
||||
export function collectTokenIdsFromProject(project: Project): TokenId[] {
|
||||
const ids = new Set<string>();
|
||||
for (const scene of Object.values(project.scenes)) {
|
||||
for (const t of scene.tokens ?? []) {
|
||||
if (t.tokenId) ids.add(t.tokenId);
|
||||
}
|
||||
}
|
||||
return [...ids].map((id) => asTokenId(id));
|
||||
}
|
||||
|
||||
/** Переписать tokenId на сценах после импорта в app-пул. */
|
||||
export function remapProjectSceneTokenIds(
|
||||
project: Project,
|
||||
remap: ReadonlyMap<string, string>,
|
||||
): Project {
|
||||
if (remap.size === 0) return project;
|
||||
const scenes: Record<SceneId, Scene> = { ...project.scenes };
|
||||
for (const sid of Object.keys(scenes) as SceneId[]) {
|
||||
const scene = scenes[sid];
|
||||
if (!scene?.tokens?.length) continue;
|
||||
scenes[sid] = {
|
||||
...scene,
|
||||
tokens: scene.tokens.map((t) => ({
|
||||
...t,
|
||||
tokenId: asTokenId(remap.get(t.tokenId) ?? t.tokenId),
|
||||
})),
|
||||
};
|
||||
}
|
||||
return { ...project, scenes };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user