215 lines
6.2 KiB
TypeScript
215 lines
6.2 KiB
TypeScript
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<Storage, 'getItem' | 'setItem' | 'removeItem'> | null;
|
|
};
|
|
|
|
function resolveEnvironment(
|
|
env?: CustomWorldAgentUiEnvironment,
|
|
): Required<CustomWorldAgentUiEnvironment> {
|
|
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);
|
|
}
|