import type { CustomWorldAgentUiState } from '../types'; export const CUSTOM_WORLD_AGENT_SESSION_QUERY_KEY = 'customWorldSessionId'; export const CUSTOM_WORLD_AGENT_OPERATION_QUERY_KEY = 'customWorldOperationId'; export const CUSTOM_WORLD_GENERATION_SOURCE_QUERY_KEY = 'customWorldGenerationSource'; export const CUSTOM_WORLD_AGENT_UI_STATE_STORAGE_KEY = 'genarrative.custom-world-agent-ui.v1'; type CustomWorldAgentUiEnvironment = { location?: { pathname: string; search: string; } | null; history?: { replaceState: ( data: unknown, unused: string, url?: string | URL | null, ) => void; } | null; sessionStorage?: Pick | null; }; function resolveEnvironment( env?: CustomWorldAgentUiEnvironment, ): Required { if (env) { return { location: env.location ?? null, history: env.history ?? null, sessionStorage: env.sessionStorage ?? null, }; } if (typeof window === 'undefined') { return { location: null, history: null, sessionStorage: null, }; } return { location: window.location, history: window.history, sessionStorage: window.sessionStorage, }; } function normalizeValue(value: unknown) { return typeof value === 'string' && value.trim() ? value.trim() : null; } function normalizeGenerationSource(value: unknown) { return value === 'agent-draft-foundation' ? value : null; } function hasExplicitAgentUiStateQuery( params: URLSearchParams, ) { return ( params.has(CUSTOM_WORLD_AGENT_SESSION_QUERY_KEY) || params.has(CUSTOM_WORLD_AGENT_OPERATION_QUERY_KEY) || params.has(CUSTOM_WORLD_GENERATION_SOURCE_QUERY_KEY) ); } function normalizePathname(value: string | undefined) { const pathname = value?.trim().toLowerCase() ?? ''; if (!pathname || pathname === '/') { return '/'; } return pathname.replace(/\/+$/u, ''); } function isRpgCreationRestorePath(pathname: string | undefined) { const normalizedPathname = normalizePathname(pathname); return ( normalizedPathname === '/creation/rpg' || normalizedPathname.startsWith('/creation/rpg/') ); } export function shouldRestoreCustomWorldAgentUiState( env?: CustomWorldAgentUiEnvironment, ) { const resolved = resolveEnvironment(env); const params = new URLSearchParams(resolved.location?.search ?? ''); // URL 显式恢复参数优先于当前路径,用于支持外部分享或登录回跳后的深链恢复。 if (hasExplicitAgentUiStateQuery(params)) { return true; } // sessionStorage 里的残留指针只能在 RPG 创作页面生效, // 避免刷新平台首页时被旧工作区状态强制带到 Agent 页面。 return isRpgCreationRestorePath(resolved.location?.pathname); } export function readCustomWorldAgentUiState( env?: CustomWorldAgentUiEnvironment, ): CustomWorldAgentUiState { const resolved = resolveEnvironment(env); const params = new URLSearchParams(resolved.location?.search ?? ''); const stateFromQuery: CustomWorldAgentUiState = { activeSessionId: normalizeValue( params.get(CUSTOM_WORLD_AGENT_SESSION_QUERY_KEY), ), activeOperationId: normalizeValue( params.get(CUSTOM_WORLD_AGENT_OPERATION_QUERY_KEY), ), customWorldGenerationSource: normalizeGenerationSource( params.get(CUSTOM_WORLD_GENERATION_SOURCE_QUERY_KEY), ), }; if ( stateFromQuery.activeSessionId || stateFromQuery.activeOperationId || stateFromQuery.customWorldGenerationSource ) { return stateFromQuery; } const storedValue = resolved.sessionStorage?.getItem( CUSTOM_WORLD_AGENT_UI_STATE_STORAGE_KEY, ); if (!storedValue) { return {}; } try { const parsed = JSON.parse(storedValue) as CustomWorldAgentUiState; return { activeSessionId: normalizeValue(parsed.activeSessionId), activeOperationId: normalizeValue(parsed.activeOperationId), customWorldGenerationSource: normalizeGenerationSource( parsed.customWorldGenerationSource, ), ownerUserId: normalizeValue(parsed.ownerUserId), }; } catch { resolved.sessionStorage?.removeItem(CUSTOM_WORLD_AGENT_UI_STATE_STORAGE_KEY); return {}; } } export function writeCustomWorldAgentUiState( state: CustomWorldAgentUiState, env?: CustomWorldAgentUiEnvironment, ) { const resolved = resolveEnvironment(env); const activeSessionId = normalizeValue(state.activeSessionId); const activeOperationId = normalizeValue(state.activeOperationId); const customWorldGenerationSource = normalizeGenerationSource( state.customWorldGenerationSource, ); const ownerUserId = normalizeValue(state.ownerUserId); const nextState: CustomWorldAgentUiState = { activeSessionId, activeOperationId, customWorldGenerationSource, ownerUserId, }; if (resolved.location && resolved.history?.replaceState) { const params = new URLSearchParams(resolved.location.search); if (activeSessionId) { params.set(CUSTOM_WORLD_AGENT_SESSION_QUERY_KEY, activeSessionId); } else { params.delete(CUSTOM_WORLD_AGENT_SESSION_QUERY_KEY); } if (activeOperationId) { params.set(CUSTOM_WORLD_AGENT_OPERATION_QUERY_KEY, activeOperationId); } else { params.delete(CUSTOM_WORLD_AGENT_OPERATION_QUERY_KEY); } if (customWorldGenerationSource) { params.set( CUSTOM_WORLD_GENERATION_SOURCE_QUERY_KEY, customWorldGenerationSource, ); } else { params.delete(CUSTOM_WORLD_GENERATION_SOURCE_QUERY_KEY); } const search = params.toString(); const nextUrl = search ? `${resolved.location.pathname}?${search}` : resolved.location.pathname; resolved.history.replaceState(null, '', nextUrl); } if (resolved.sessionStorage) { if (activeSessionId || activeOperationId || customWorldGenerationSource) { resolved.sessionStorage.setItem( CUSTOM_WORLD_AGENT_UI_STATE_STORAGE_KEY, JSON.stringify(nextState), ); } else { resolved.sessionStorage.removeItem(CUSTOM_WORLD_AGENT_UI_STATE_STORAGE_KEY); } } } export function clearCustomWorldAgentUiState( env?: CustomWorldAgentUiEnvironment, ) { writeCustomWorldAgentUiState({}, env); }