Files
Genarrative/apps/ai-game-creator-shell/src/view/project-development/projectResourceLiveUpdateModel.ts
T
menghao 03a4410272
Project CI / Repository checks (push) Successful in 1m5s
Project CI / Frontend tests (push) Successful in 3m20s
Project CI / Backend tests (push) Successful in 4m1s
Project CI / Native shell tests (push) Failing after 11m56s
game agent无限画布开发 (#136)
主要工作是共享同一套“画布内核与通用 UI 源码”,网站和 Tauri 只分别实现宿主适配层。

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/136
Co-authored-by: menghao <mh18530625731@163.com>
Co-committed-by: menghao <mh18530625731@163.com>
2026-08-12 10:54:16 +08:00

200 lines
4.7 KiB
TypeScript

import type { GameCreationAppManifest } from '../../../../../packages/shared/src/contracts/gameCreationApp';
export type ProjectManifestSnapshotSource =
| 'initial'
| 'supervisor'
| 'asset-command'
| 'asset-event'
| 'poll';
export type ProjectManifestSnapshot = {
projectPath: string;
projectId: string;
revision: number;
manifest: GameCreationAppManifest;
source: ProjectManifestSnapshotSource;
commitId?: string;
eventId?: string;
};
export type ProjectManifestSnapshotMetadata = Omit<
ProjectManifestSnapshot,
'projectPath' | 'manifest'
>;
export type ProjectManifestMergeState = {
projectPath: string;
projectId: string;
revision: number;
manifestFingerprint: string;
seenCommitRevisions: string[];
seenEventIds: string[];
};
export type ProjectManifestMergeDecision =
| 'accepted'
| 'duplicate'
| 'stale-revision'
| 'scope-mismatch'
| 'revision-conflict';
const MAX_DEDUPE_IDENTITIES = 256;
function appendBounded(values: string[], value: string | undefined) {
if (!value || values.includes(value)) {
return values;
}
return [...values, value].slice(-MAX_DEDUPE_IDENTITIES);
}
function manifestFingerprint(manifest: GameCreationAppManifest) {
return JSON.stringify(manifest);
}
function commitRevisionIdentity(snapshot: ProjectManifestSnapshot) {
return snapshot.commitId
? `${snapshot.projectId}:${snapshot.commitId}:${snapshot.revision}`
: undefined;
}
function withDedupeIdentities(
state: ProjectManifestMergeState,
snapshot: ProjectManifestSnapshot,
) {
return {
...state,
seenCommitRevisions: appendBounded(
state.seenCommitRevisions,
commitRevisionIdentity(snapshot),
),
seenEventIds: appendBounded(state.seenEventIds, snapshot.eventId),
};
}
export function createProjectManifestMergeState(
snapshot: ProjectManifestSnapshot,
): ProjectManifestMergeState {
return withDedupeIdentities(
{
projectPath: snapshot.projectPath,
projectId: snapshot.projectId,
revision: snapshot.revision,
manifestFingerprint: manifestFingerprint(snapshot.manifest),
seenCommitRevisions: [],
seenEventIds: [],
},
snapshot,
);
}
export function mergeProjectManifestSnapshot(
current: ProjectManifestMergeState,
snapshot: ProjectManifestSnapshot,
): {
decision: ProjectManifestMergeDecision;
state: ProjectManifestMergeState;
} {
if (
snapshot.projectPath !== current.projectPath ||
snapshot.projectId !== current.projectId ||
snapshot.manifest.projectId !== snapshot.projectId
) {
return { decision: 'scope-mismatch', state: current };
}
if (snapshot.revision < current.revision) {
return { decision: 'stale-revision', state: current };
}
const fingerprint = manifestFingerprint(snapshot.manifest);
if (snapshot.revision === current.revision) {
if (fingerprint !== current.manifestFingerprint) {
return { decision: 'revision-conflict', state: current };
}
return {
decision: 'duplicate',
state: withDedupeIdentities(current, snapshot),
};
}
return {
decision: 'accepted',
state: withDedupeIdentities(
{
...current,
revision: snapshot.revision,
manifestFingerprint: fingerprint,
},
snapshot,
),
};
}
export type ResourceFocusIntent = {
flowId: string;
saveAttemptId: string;
sessionId: string;
draftId: string;
commitId: string;
projectPath: string;
projectId: string;
focusGeneration: number;
resourceId: string | null;
completed: boolean;
};
export type ResourceFocusResolution =
| 'invalid'
| 'wait-projection'
| 'wait-layout'
| 'hidden'
| 'wait-dom'
| 'focus'
| 'completed';
export function resolveResourceFocusIntent(
intent: ResourceFocusIntent,
input: {
projectPath: string;
projectId: string;
flowId: string | null;
focusGeneration: number;
projected: boolean;
dependencyLayoutSettled: boolean;
dependencyPositioned: boolean;
typeLayoutSettled: boolean;
typePositioned: boolean;
visible: boolean;
domRendered: boolean;
},
): ResourceFocusResolution {
if (intent.completed) {
return 'completed';
}
if (
intent.projectPath !== input.projectPath ||
intent.projectId !== input.projectId ||
intent.flowId !== input.flowId ||
intent.focusGeneration !== input.focusGeneration
) {
return 'invalid';
}
if (!intent.resourceId || !input.projected) {
return 'wait-projection';
}
if (
!input.dependencyLayoutSettled ||
!input.typeLayoutSettled ||
!input.dependencyPositioned ||
!input.typePositioned
) {
return 'wait-layout';
}
if (!input.visible) {
return 'hidden';
}
if (!input.domRendered) {
return 'wait-dom';
}
return 'focus';
}