export const ISOLATED_CHAT_ROOM_SESSION_PREFIX = 'chat-room-menu-'; export const MANAGED_CHAT_SHARE_SESSION_PREFIX = 'chat-share-room-'; export type IsolatedChatRoomScope = { topMenu: string; menuTitle: string; featureTitle: string; focusedComponentId: string | null; pageUrl: string; selectionSummary?: string | null; selectionIds?: string[]; errorSummary?: string | null; sourceAppId?: string | null; launchedAt: string; }; export type MainChatPanelMode = 'live' | 'rooms'; export function isIsolatedChatRoomSessionId(sessionId: string | null | undefined) { return String(sessionId ?? '').trim().startsWith(ISOLATED_CHAT_ROOM_SESSION_PREFIX); } export function isManagedChatShareSessionId(sessionId: string | null | undefined) { return String(sessionId ?? '').trim().startsWith(MANAGED_CHAT_SHARE_SESSION_PREFIX); } export function createIsolatedChatRoomSessionId() { if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { return `${ISOLATED_CHAT_ROOM_SESSION_PREFIX}${crypto.randomUUID()}`; } return `${ISOLATED_CHAT_ROOM_SESSION_PREFIX}${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`; } export function shouldShowConversationForMode(sessionId: string, mode: MainChatPanelMode) { const isIsolatedRoom = isIsolatedChatRoomSessionId(sessionId); const isManagedShareRoom = isManagedChatShareSessionId(sessionId); return mode === 'rooms' ? isIsolatedRoom || isManagedShareRoom : !isIsolatedRoom && !isManagedShareRoom; } export function resolveChatPathForSession(sessionId: string) { return isIsolatedChatRoomSessionId(sessionId) || isManagedChatShareSessionId(sessionId) ? '/chat/rooms' : '/chat/live'; } export function normalizeIsolatedChatRoomScope( scope: Partial | null | undefined, ): IsolatedChatRoomScope | null { if (!scope) { return null; } const menuTitle = String(scope.menuTitle ?? '').trim(); const featureTitle = String(scope.featureTitle ?? '').trim(); const pageUrl = String(scope.pageUrl ?? '').trim(); if (!menuTitle && !featureTitle && !pageUrl) { return null; } return { topMenu: String(scope.topMenu ?? '').trim() || 'unknown', menuTitle: menuTitle || '현재 메뉴', featureTitle: featureTitle || menuTitle || '현재 기능', focusedComponentId: String(scope.focusedComponentId ?? '').trim() || null, pageUrl, selectionSummary: String(scope.selectionSummary ?? '').trim() || null, selectionIds: Array.isArray(scope.selectionIds) ? scope.selectionIds.map((item) => String(item).trim()).filter(Boolean) : [], errorSummary: String(scope.errorSummary ?? '').trim() || null, sourceAppId: String(scope.sourceAppId ?? '').trim() || null, launchedAt: String(scope.launchedAt ?? '').trim() || new Date().toISOString(), }; } export function buildIsolatedChatRoomTitle(scope: IsolatedChatRoomScope | null | undefined) { if (!scope) { return '격리 채팅방'; } return `${scope.menuTitle} · ${scope.featureTitle}`.trim(); } export function buildIsolatedChatRoomRequestBadgeLabel(scope: IsolatedChatRoomScope | null | undefined) { if (!scope) { return '격리 요청'; } return scope.focusedComponentId?.trim() || scope.featureTitle || scope.menuTitle || '격리 요청'; } export function buildIsolatedChatRoomContextSupplement(scope: IsolatedChatRoomScope | null | undefined) { if (!scope) { return ''; } const lines = [ '## 격리 채팅방 범위', `- 현재 활성 메뉴: ${scope.menuTitle}`, `- 현재 기능: ${scope.featureTitle}`, `- topMenu: ${scope.topMenu || '없음'}`, `- focusedComponentId: ${scope.focusedComponentId || '없음'}`, `- pageUrl: ${scope.pageUrl || '없음'}`, ]; if (scope.selectionSummary) { lines.push(`- 현재 선택: ${scope.selectionSummary}`); } if (scope.selectionIds && scope.selectionIds.length > 0) { lines.push(`- 선택 ID: ${scope.selectionIds.join(', ')}`); } if (scope.errorSummary) { lines.push(''); lines.push('## 최근 참조 에러'); lines.push(scope.errorSummary); } return lines.join('\n').trim(); } function normalizeScopeCompareValue(value: string | null | undefined) { return String(value ?? '').trim(); } function normalizeScopeSelectionIds(value: string[] | null | undefined) { return (value ?? []).map((item) => String(item).trim()).filter(Boolean).sort(); } export function doesIsolatedChatRoomScopeMatch( left: IsolatedChatRoomScope | null | undefined, right: IsolatedChatRoomScope | null | undefined, ) { const normalizedLeft = normalizeIsolatedChatRoomScope(left); const normalizedRight = normalizeIsolatedChatRoomScope(right); if (!normalizedLeft || !normalizedRight) { return false; } const leftSelectionIds = normalizeScopeSelectionIds(normalizedLeft.selectionIds); const rightSelectionIds = normalizeScopeSelectionIds(normalizedRight.selectionIds); return ( normalizeScopeCompareValue(normalizedLeft.sourceAppId) === normalizeScopeCompareValue(normalizedRight.sourceAppId) && normalizeScopeCompareValue(normalizedLeft.topMenu) === normalizeScopeCompareValue(normalizedRight.topMenu) && normalizeScopeCompareValue(normalizedLeft.menuTitle) === normalizeScopeCompareValue(normalizedRight.menuTitle) && normalizeScopeCompareValue(normalizedLeft.featureTitle) === normalizeScopeCompareValue(normalizedRight.featureTitle) && normalizeScopeCompareValue(normalizedLeft.focusedComponentId) === normalizeScopeCompareValue(normalizedRight.focusedComponentId) && normalizeScopeCompareValue(normalizedLeft.pageUrl) === normalizeScopeCompareValue(normalizedRight.pageUrl) && leftSelectionIds.length === rightSelectionIds.length && leftSelectionIds.every((item, index) => item === rightSelectionIds[index]) ); }