Enforce Genarrative play-type SOP and update docs
Rewrite Genarrative play-type integration guidance across .codex and .hermes to define a platform-level SOP: default to form/image workbench, unify single-image asset slots (CreativeImageInputPanel), standardize series-material sheet->cut->transparent->OSS pipeline, and forbid copying legacy chat/agent workflows as the default. Add decision-log entry freezing the SOP and a pitfalls note warning against direct reuse of old play tools. Update CONTEXT.md and docs/README.md, add a new PRD file, and apply related small server-side changes (module-auth, spacetime-client mappers and runtime) to align back-end code with the new contracts and flows.
This commit is contained in:
274
src/services/jump-hop/jumpHopClient.ts
Normal file
274
src/services/jump-hop/jumpHopClient.ts
Normal file
@@ -0,0 +1,274 @@
|
||||
import type {
|
||||
JumpHopActionRequest,
|
||||
JumpHopActionResponse,
|
||||
JumpHopDraftResponse,
|
||||
JumpHopGalleryCardResponse,
|
||||
JumpHopGalleryDetailResponse,
|
||||
JumpHopGalleryResponse,
|
||||
JumpHopRunResponse,
|
||||
JumpHopRuntimeRunSnapshotResponse,
|
||||
JumpHopSessionResponse,
|
||||
JumpHopSessionSnapshotResponse,
|
||||
JumpHopWorkDetailResponse,
|
||||
JumpHopWorkMutationResponse,
|
||||
JumpHopWorkProfileResponse,
|
||||
JumpHopWorkspaceCreateRequest,
|
||||
JumpHopWorkSummaryResponse,
|
||||
} from '../../../packages/shared/src/contracts/jumpHop';
|
||||
import { type ApiRetryOptions, requestJson } from '../apiClient';
|
||||
import { createCreationAgentClient } from '../creation-agent';
|
||||
|
||||
const JUMP_HOP_API_BASE = '/api/creation/jump-hop/sessions';
|
||||
const JUMP_HOP_WORKS_API_BASE = '/api/creation/jump-hop/works';
|
||||
const JUMP_HOP_RUNTIME_API_BASE = '/api/runtime/jump-hop';
|
||||
const JUMP_HOP_RUNTIME_READ_RETRY: ApiRetryOptions = {
|
||||
maxRetries: 1,
|
||||
baseDelayMs: 120,
|
||||
maxDelayMs: 360,
|
||||
};
|
||||
|
||||
export type {
|
||||
JumpHopActionRequest,
|
||||
JumpHopActionResponse,
|
||||
JumpHopDraftResponse,
|
||||
JumpHopGalleryCardResponse,
|
||||
JumpHopGalleryDetailResponse,
|
||||
JumpHopGalleryResponse,
|
||||
JumpHopRunResponse,
|
||||
JumpHopRuntimeRunSnapshotResponse,
|
||||
JumpHopSessionResponse,
|
||||
JumpHopSessionSnapshotResponse,
|
||||
JumpHopWorkDetailResponse,
|
||||
JumpHopWorkMutationResponse,
|
||||
JumpHopWorkProfileResponse,
|
||||
JumpHopWorkspaceCreateRequest,
|
||||
};
|
||||
export type CreateJumpHopSessionRequest = {
|
||||
themeText: string;
|
||||
characterDescription: string;
|
||||
tileStyle: string;
|
||||
difficulty: string;
|
||||
rhythmPreference: string;
|
||||
};
|
||||
export type ExecuteJumpHopActionRequest = JumpHopActionRequest;
|
||||
export type JumpHopSessionSnapshot = JumpHopSessionSnapshotResponse;
|
||||
|
||||
const jumpHopCreationClient = createCreationAgentClient<
|
||||
JumpHopWorkspaceCreateRequest,
|
||||
JumpHopSessionResponse,
|
||||
JumpHopSessionResponse,
|
||||
JumpHopSessionSnapshotResponse,
|
||||
never,
|
||||
never,
|
||||
JumpHopActionRequest,
|
||||
JumpHopActionResponse
|
||||
>({
|
||||
apiBase: JUMP_HOP_API_BASE,
|
||||
messages: {
|
||||
createSession: '创建跳一跳共创会话失败',
|
||||
getSession: '读取跳一跳共创会话失败',
|
||||
sendMessage: '发送跳一跳共创消息失败',
|
||||
streamIncomplete: '跳一跳共创消息流式结果不完整',
|
||||
executeAction: '执行跳一跳共创操作失败',
|
||||
},
|
||||
});
|
||||
|
||||
type FlattenedJumpHopWorkProfileResponse = Omit<
|
||||
JumpHopWorkProfileResponse,
|
||||
'summary'
|
||||
> &
|
||||
JumpHopWorkSummaryResponse;
|
||||
|
||||
function normalizeJumpHopWorkProfile(
|
||||
work: JumpHopWorkProfileResponse | FlattenedJumpHopWorkProfileResponse,
|
||||
): JumpHopWorkProfileResponse {
|
||||
if ('summary' in work && work.summary) {
|
||||
return work;
|
||||
}
|
||||
|
||||
const flattened = work as FlattenedJumpHopWorkProfileResponse;
|
||||
const summary: JumpHopWorkProfileResponse['summary'] = {
|
||||
runtimeKind: flattened.runtimeKind,
|
||||
workId: flattened.workId,
|
||||
profileId: flattened.profileId,
|
||||
ownerUserId: flattened.ownerUserId,
|
||||
sourceSessionId: flattened.sourceSessionId ?? null,
|
||||
workTitle: flattened.workTitle,
|
||||
workDescription: flattened.workDescription,
|
||||
themeTags: flattened.themeTags,
|
||||
difficulty: flattened.difficulty,
|
||||
stylePreset: flattened.stylePreset,
|
||||
coverImageSrc: flattened.coverImageSrc ?? null,
|
||||
publicationStatus: flattened.publicationStatus,
|
||||
playCount: flattened.playCount,
|
||||
updatedAt: flattened.updatedAt,
|
||||
publishedAt: flattened.publishedAt ?? null,
|
||||
publishReady: flattened.publishReady,
|
||||
generationStatus: flattened.generationStatus,
|
||||
};
|
||||
|
||||
return {
|
||||
summary,
|
||||
draft: flattened.draft,
|
||||
path: flattened.path,
|
||||
characterAsset: flattened.characterAsset,
|
||||
tileAtlasAsset: flattened.tileAtlasAsset,
|
||||
tileAssets: flattened.tileAssets,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeJumpHopActionResponse(
|
||||
response: JumpHopActionResponse,
|
||||
): JumpHopActionResponse {
|
||||
return {
|
||||
...response,
|
||||
work: response.work ? normalizeJumpHopWorkProfile(response.work) : null,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeJumpHopWorkDetailResponse(
|
||||
response: JumpHopWorkDetailResponse,
|
||||
): JumpHopWorkDetailResponse {
|
||||
return {
|
||||
...response,
|
||||
item: normalizeJumpHopWorkProfile(response.item),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeJumpHopWorkMutationResponse(
|
||||
response: JumpHopWorkMutationResponse,
|
||||
): JumpHopWorkMutationResponse {
|
||||
return {
|
||||
...response,
|
||||
item: normalizeJumpHopWorkProfile(response.item),
|
||||
};
|
||||
}
|
||||
|
||||
export function createJumpHopCreationSession(
|
||||
payload: JumpHopWorkspaceCreateRequest,
|
||||
) {
|
||||
return jumpHopCreationClient.createSession(payload);
|
||||
}
|
||||
|
||||
export function getJumpHopCreationSession(sessionId: string) {
|
||||
return jumpHopCreationClient.getSession(sessionId);
|
||||
}
|
||||
|
||||
export function executeJumpHopCreationAction(
|
||||
sessionId: string,
|
||||
payload: JumpHopActionRequest,
|
||||
) {
|
||||
return jumpHopCreationClient
|
||||
.executeAction(sessionId, payload)
|
||||
.then(normalizeJumpHopActionResponse);
|
||||
}
|
||||
|
||||
export async function getJumpHopWorkDetail(profileId: string) {
|
||||
const response = await requestJson<JumpHopWorkDetailResponse>(
|
||||
`${JUMP_HOP_RUNTIME_API_BASE}/works/${encodeURIComponent(profileId)}`,
|
||||
{ method: 'GET' },
|
||||
'读取跳一跳作品详情失败',
|
||||
);
|
||||
return normalizeJumpHopWorkDetailResponse(response);
|
||||
}
|
||||
|
||||
export async function listJumpHopGallery() {
|
||||
return requestJson<JumpHopGalleryResponse>(
|
||||
`${JUMP_HOP_RUNTIME_API_BASE}/gallery`,
|
||||
{ method: 'GET' },
|
||||
'读取跳一跳广场失败',
|
||||
{
|
||||
retry: JUMP_HOP_RUNTIME_READ_RETRY,
|
||||
skipAuth: true,
|
||||
skipRefresh: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function getJumpHopGalleryDetail(publicWorkCode: string) {
|
||||
const response = await requestJson<JumpHopGalleryDetailResponse>(
|
||||
`${JUMP_HOP_RUNTIME_API_BASE}/gallery/${encodeURIComponent(publicWorkCode)}`,
|
||||
{ method: 'GET' },
|
||||
'读取跳一跳广场详情失败',
|
||||
{
|
||||
retry: JUMP_HOP_RUNTIME_READ_RETRY,
|
||||
skipAuth: true,
|
||||
skipRefresh: true,
|
||||
},
|
||||
);
|
||||
return normalizeJumpHopWorkDetailResponse(response);
|
||||
}
|
||||
|
||||
export async function publishJumpHopWork(profileId: string) {
|
||||
const response = await requestJson<JumpHopWorkMutationResponse>(
|
||||
`${JUMP_HOP_WORKS_API_BASE}/${encodeURIComponent(profileId)}/publish`,
|
||||
{ method: 'POST' },
|
||||
'发布跳一跳作品失败',
|
||||
);
|
||||
return normalizeJumpHopWorkMutationResponse(response);
|
||||
}
|
||||
|
||||
export async function startJumpHopRuntimeRun(profileId: string) {
|
||||
return requestJson<JumpHopRunResponse>(
|
||||
`${JUMP_HOP_RUNTIME_API_BASE}/runs`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ profileId }),
|
||||
},
|
||||
'启动跳一跳运行态失败',
|
||||
);
|
||||
}
|
||||
|
||||
export async function submitJumpHopJump(
|
||||
runId: string,
|
||||
payload: { chargeMs: number },
|
||||
) {
|
||||
const requestPayload = {
|
||||
chargeMs: payload.chargeMs,
|
||||
clientEventId: `jump-${runId}-${Date.now()}`,
|
||||
};
|
||||
|
||||
return requestJson<JumpHopRunResponse>(
|
||||
`${JUMP_HOP_RUNTIME_API_BASE}/runs/${encodeURIComponent(runId)}/jump`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(requestPayload),
|
||||
},
|
||||
'提交跳一跳起跳失败',
|
||||
);
|
||||
}
|
||||
|
||||
export async function restartJumpHopRuntimeRun(runId: string) {
|
||||
return requestJson<JumpHopRunResponse>(
|
||||
`${JUMP_HOP_RUNTIME_API_BASE}/runs/${encodeURIComponent(runId)}/restart`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
clientActionId: `restart-${runId}-${Date.now()}`,
|
||||
}),
|
||||
},
|
||||
'重新开始跳一跳失败',
|
||||
);
|
||||
}
|
||||
|
||||
export const jumpHopClient = {
|
||||
createSession: createJumpHopCreationSession,
|
||||
getSession: getJumpHopCreationSession,
|
||||
executeAction: executeJumpHopCreationAction,
|
||||
getGalleryDetail: getJumpHopGalleryDetail,
|
||||
getWorkDetail: getJumpHopWorkDetail,
|
||||
listGallery: listJumpHopGallery,
|
||||
publishWork: publishJumpHopWork,
|
||||
restartRun: restartJumpHopRuntimeRun,
|
||||
startRun: startJumpHopRuntimeRun,
|
||||
submitJump: submitJumpHopJump,
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
buildBabyObjectMatchGenerationAnchorEntries,
|
||||
buildJumpHopGenerationAnchorEntries,
|
||||
buildMatch3DGenerationAnchorEntries,
|
||||
buildMiniGameDraftGenerationProgress,
|
||||
buildPuzzleGenerationAnchorEntries,
|
||||
@@ -306,6 +307,54 @@ describe('miniGameDraftGenerationProgress', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test('jump hop draft generation exposes character and tile atlas pipeline', () => {
|
||||
const state = createMiniGameDraftGenerationState('jump-hop');
|
||||
|
||||
const progress = buildMiniGameDraftGenerationProgress(
|
||||
state,
|
||||
state.startedAtMs + 35_000,
|
||||
);
|
||||
|
||||
expect(progress?.steps.map((step) => step.id)).toEqual([
|
||||
'jump-hop-draft',
|
||||
'jump-hop-character',
|
||||
'jump-hop-tile-atlas',
|
||||
'jump-hop-slice-tiles',
|
||||
'jump-hop-write-draft',
|
||||
]);
|
||||
expect(progress?.phaseId).toBe('jump-hop-character');
|
||||
expect(progress?.phaseLabel).toBe('生成角色形象');
|
||||
expect(progress?.estimatedRemainingMs).toBe(265_000);
|
||||
});
|
||||
|
||||
test('jump hop generation anchors expose theme, character and tile style', () => {
|
||||
const entries = buildJumpHopGenerationAnchorEntries(null, {
|
||||
themeText: '云端糖果塔',
|
||||
characterDescription: '披着星星披风的小旅人',
|
||||
tileStyle: '纸模玩具',
|
||||
difficulty: '标准',
|
||||
rhythmPreference: '轻快',
|
||||
});
|
||||
|
||||
expect(entries).toEqual([
|
||||
{
|
||||
id: 'jump-hop-theme',
|
||||
label: '主题',
|
||||
value: '云端糖果塔',
|
||||
},
|
||||
{
|
||||
id: 'jump-hop-character',
|
||||
label: '角色',
|
||||
value: '披着星星披风的小旅人',
|
||||
},
|
||||
{
|
||||
id: 'jump-hop-tile-style',
|
||||
label: '地块',
|
||||
value: '纸模玩具',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('puzzle generation anchors expose form payload as the display source', () => {
|
||||
const entries = buildPuzzleGenerationAnchorEntries({
|
||||
sessionId: 'puzzle-session-1',
|
||||
|
||||
@@ -17,13 +17,18 @@ import type {
|
||||
} from '../../packages/shared/src/contracts/runtime';
|
||||
import type { SquareHoleSessionSnapshot } from '../../packages/shared/src/contracts/squareHoleAgent';
|
||||
import type { CustomWorldStructuredAnchorEntry } from './customWorldAgentGenerationProgress';
|
||||
import type {
|
||||
CreateJumpHopSessionRequest,
|
||||
JumpHopSessionSnapshot,
|
||||
} from './jump-hop/jumpHopClient';
|
||||
|
||||
export type MiniGameDraftGenerationKind =
|
||||
| 'puzzle'
|
||||
| 'big-fish'
|
||||
| 'square-hole'
|
||||
| 'match3d'
|
||||
| 'baby-object-match';
|
||||
| 'baby-object-match'
|
||||
| 'jump-hop';
|
||||
|
||||
export type MiniGameDraftGenerationPhase =
|
||||
| 'idle'
|
||||
@@ -49,6 +54,11 @@ export type MiniGameDraftGenerationPhase =
|
||||
| 'baby-object-draft'
|
||||
| 'baby-object-images'
|
||||
| 'baby-object-ready'
|
||||
| 'jump-hop-draft'
|
||||
| 'jump-hop-character'
|
||||
| 'jump-hop-tile-atlas'
|
||||
| 'jump-hop-slice-tiles'
|
||||
| 'jump-hop-write-draft'
|
||||
| 'puzzle-images'
|
||||
| 'puzzle-ui-background'
|
||||
| 'puzzle-select-image'
|
||||
@@ -268,6 +278,41 @@ const BABY_OBJECT_MATCH_STEPS = [
|
||||
},
|
||||
] as const satisfies ReadonlyArray<MiniGameStepDefinition>;
|
||||
|
||||
const JUMP_HOP_STEPS = [
|
||||
{
|
||||
id: 'jump-hop-draft',
|
||||
label: '整理玩法草稿',
|
||||
detail: '建立主题、难度和路径基础数据。',
|
||||
weight: 10,
|
||||
},
|
||||
{
|
||||
id: 'jump-hop-character',
|
||||
label: '生成角色形象',
|
||||
detail: '生成可进入运行态的俯视角角色图。',
|
||||
weight: 34,
|
||||
},
|
||||
{
|
||||
id: 'jump-hop-tile-atlas',
|
||||
label: '生成地块图集',
|
||||
detail: '生成起点、普通、目标和终点地块图集。',
|
||||
weight: 34,
|
||||
},
|
||||
{
|
||||
id: 'jump-hop-slice-tiles',
|
||||
label: '切分地块素材',
|
||||
detail: '切分透明地块 PNG 并校验落点半径。',
|
||||
weight: 14,
|
||||
},
|
||||
{
|
||||
id: 'jump-hop-write-draft',
|
||||
label: '写入正式草稿',
|
||||
detail: '保存角色、地块、路径和封面合成结果。',
|
||||
weight: 8,
|
||||
},
|
||||
] as const satisfies ReadonlyArray<MiniGameStepDefinition>;
|
||||
|
||||
const JUMP_HOP_ESTIMATED_WAIT_MS = 5 * 60_000;
|
||||
|
||||
function clampProgress(value: number) {
|
||||
return Math.max(0, Math.min(100, Math.round(value)));
|
||||
}
|
||||
@@ -285,6 +330,9 @@ function getStepDefinitions(kind: MiniGameDraftGenerationKind) {
|
||||
if (kind === 'baby-object-match') {
|
||||
return BABY_OBJECT_MATCH_STEPS;
|
||||
}
|
||||
if (kind === 'jump-hop') {
|
||||
return JUMP_HOP_STEPS;
|
||||
}
|
||||
return BIG_FISH_STEPS;
|
||||
}
|
||||
|
||||
@@ -340,8 +388,10 @@ export function createMiniGameDraftGenerationState(
|
||||
? 'square-hole-draft'
|
||||
: kind === 'match3d'
|
||||
? 'match3d-work-title'
|
||||
: kind === 'baby-object-match'
|
||||
? 'baby-object-draft'
|
||||
: kind === 'baby-object-match'
|
||||
? 'baby-object-draft'
|
||||
: kind === 'jump-hop'
|
||||
? 'jump-hop-draft'
|
||||
: 'compile',
|
||||
startedAtMs: Date.now(),
|
||||
completedAssetCount: 0,
|
||||
@@ -413,6 +463,24 @@ function resolveBabyObjectMatchPhaseByElapsedMs(
|
||||
return 'baby-object-draft';
|
||||
}
|
||||
|
||||
function resolveJumpHopPhaseByElapsedMs(
|
||||
elapsedMs: number,
|
||||
): MiniGameDraftGenerationPhase {
|
||||
if (elapsedMs >= 270_000) {
|
||||
return 'jump-hop-write-draft';
|
||||
}
|
||||
if (elapsedMs >= 220_000) {
|
||||
return 'jump-hop-slice-tiles';
|
||||
}
|
||||
if (elapsedMs >= 115_000) {
|
||||
return 'jump-hop-tile-atlas';
|
||||
}
|
||||
if (elapsedMs >= 12_000) {
|
||||
return 'jump-hop-character';
|
||||
}
|
||||
return 'jump-hop-draft';
|
||||
}
|
||||
|
||||
function resolvePuzzleTimelineByElapsedMs(elapsedMs: number) {
|
||||
let elapsedBeforePhase = 0;
|
||||
|
||||
@@ -491,7 +559,14 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
...state,
|
||||
phase: resolveBabyObjectMatchPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state;
|
||||
: state.kind === 'jump-hop' &&
|
||||
state.phase !== 'failed' &&
|
||||
state.phase !== 'ready'
|
||||
? {
|
||||
...state,
|
||||
phase: resolveJumpHopPhaseByElapsedMs(elapsedMs),
|
||||
}
|
||||
: state;
|
||||
|
||||
const steps = getStepDefinitions(normalizedState.kind);
|
||||
const activeStepIndex = getActiveStepIndex(steps, normalizedState.phase);
|
||||
@@ -518,9 +593,11 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
? 0.42
|
||||
: normalizedState.kind === 'match3d'
|
||||
? 0.5
|
||||
: normalizedState.kind === 'baby-object-match'
|
||||
? 0.52
|
||||
: 0;
|
||||
: normalizedState.kind === 'baby-object-match'
|
||||
? 0.52
|
||||
: normalizedState.kind === 'jump-hop'
|
||||
? 0.5
|
||||
: 0;
|
||||
const overallProgress =
|
||||
normalizedState.phase === 'failed'
|
||||
? Math.max(1, completedWeight)
|
||||
@@ -551,6 +628,8 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
? '抓大鹅素材与草稿已准备完成,可进入结果页继续编辑。'
|
||||
: normalizedState.kind === 'baby-object-match'
|
||||
? '宝贝识物草稿已准备完成,可进入结果页继续发布。'
|
||||
: normalizedState.kind === 'jump-hop'
|
||||
? '跳一跳草稿已准备完成,可进入结果页试玩或发布。'
|
||||
: '首关草稿与正式图已准备完成,可进入结果页补作品信息。'
|
||||
: activeStep.detail),
|
||||
batchLabel: activeStep.label,
|
||||
@@ -574,6 +653,8 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
0,
|
||||
BABY_OBJECT_MATCH_ESTIMATED_WAIT_MS - elapsedMs,
|
||||
)
|
||||
: normalizedState.kind === 'jump-hop'
|
||||
? Math.max(0, JUMP_HOP_ESTIMATED_WAIT_MS - elapsedMs)
|
||||
: null,
|
||||
activeStepIndex,
|
||||
steps: buildMiniGameProgressSteps(
|
||||
@@ -585,6 +666,65 @@ export function buildMiniGameDraftGenerationProgress(
|
||||
};
|
||||
}
|
||||
|
||||
export function buildJumpHopGenerationAnchorEntries(
|
||||
session: JumpHopSessionSnapshot | null | undefined,
|
||||
formPayload: CreateJumpHopSessionRequest | null | undefined = null,
|
||||
): CustomWorldStructuredAnchorEntry[] {
|
||||
const sessionRecord = session as
|
||||
| {
|
||||
config?: Partial<CreateJumpHopSessionRequest>;
|
||||
draft?: {
|
||||
workTitle?: string;
|
||||
themeText?: string;
|
||||
characterPrompt?: string;
|
||||
stylePreset?: string;
|
||||
} | null;
|
||||
}
|
||||
| null
|
||||
| undefined;
|
||||
const config = sessionRecord?.config;
|
||||
const draft = sessionRecord?.draft;
|
||||
const entries: Array<MiniGameAnchorSource | null> = [
|
||||
{
|
||||
key: 'jump-hop-theme',
|
||||
label: '主题',
|
||||
value:
|
||||
formPayload?.themeText?.trim() ||
|
||||
config?.themeText?.trim() ||
|
||||
draft?.themeText?.trim() ||
|
||||
draft?.workTitle?.trim() ||
|
||||
'',
|
||||
},
|
||||
{
|
||||
key: 'jump-hop-character',
|
||||
label: '角色',
|
||||
value:
|
||||
formPayload?.characterDescription?.trim() ||
|
||||
config?.characterDescription?.trim() ||
|
||||
draft?.characterPrompt?.trim() ||
|
||||
'',
|
||||
},
|
||||
{
|
||||
key: 'jump-hop-tile-style',
|
||||
label: '地块',
|
||||
value:
|
||||
formPayload?.tileStyle?.trim() ||
|
||||
config?.tileStyle?.trim() ||
|
||||
draft?.stylePreset?.trim() ||
|
||||
'',
|
||||
},
|
||||
];
|
||||
|
||||
return entries
|
||||
.filter((entry): entry is MiniGameAnchorSource => Boolean(entry))
|
||||
.map((entry) => ({
|
||||
id: entry.key,
|
||||
label: entry.label,
|
||||
value: entry.value,
|
||||
}))
|
||||
.filter((entry) => entry.value.trim());
|
||||
}
|
||||
|
||||
export function buildPuzzleGenerationAnchorEntries(
|
||||
session: PuzzleAgentSessionSnapshot | null | undefined,
|
||||
formPayload: CreatePuzzleAgentSessionRequest | null | undefined = null,
|
||||
|
||||
@@ -53,6 +53,14 @@ export function buildBabyObjectMatchPublicWorkCode(profileId: string) {
|
||||
return `BO-${suffix}`;
|
||||
}
|
||||
|
||||
export function buildJumpHopPublicWorkCode(profileId: string) {
|
||||
const normalized = normalizePublicCodeText(profileId);
|
||||
const fallback = normalized || '00000000';
|
||||
const suffix = fallback.slice(-8).padStart(8, '0');
|
||||
|
||||
return `JH-${suffix}`;
|
||||
}
|
||||
|
||||
export function isSamePuzzlePublicWorkCode(keyword: string, profileId: string) {
|
||||
const normalizedKeyword = normalizePublicCodeText(keyword);
|
||||
|
||||
@@ -124,3 +132,13 @@ export function isSameBabyObjectMatchPublicWorkCode(
|
||||
normalizedKeyword === normalizePublicCodeText(profileId)
|
||||
);
|
||||
}
|
||||
|
||||
export function isSameJumpHopPublicWorkCode(keyword: string, profileId: string) {
|
||||
const normalizedKeyword = normalizePublicCodeText(keyword);
|
||||
|
||||
return (
|
||||
normalizedKeyword ===
|
||||
normalizePublicCodeText(buildJumpHopPublicWorkCode(profileId)) ||
|
||||
normalizedKeyword === normalizePublicCodeText(profileId)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user