import type { UserSelection, SlidePlan, Zone, InternalRegion, LayoutPresetId } from "../types/designAgent"; /** * Phase Z 초기 선택 상태 생성 * SlidePlan의 결과를 초기 값으로 사용 (Step 11까지의 결과 반영) */ export function createInitialUserSelection(slidePlan?: SlidePlan | null): UserSelection { const initialSections: Record = {}; const initialFrames: Record = {}; if (slidePlan) { slidePlan.zones.forEach(zone => { // 1. 모든 섹션을 각자의 지정된 존에 할당 (초안 배치) initialSections[zone.zone_id] = [...zone.section_ids]; // 2. 각 리전의 기본 frame. // 2026-05-14 — backend frame_match_strategy.frame_id 가 있을 때만 init. // null 인 경우 (backend current_default_candidate=None 등) frame_candidates[0] // 로 자동 채우지 않음 → SlideCanvas 의 preview overlay 트리거 조건 // (override !== default) 안 발동. 사용자가 직접 frame 클릭해야 preview 보임. // 배경 : 04-1 같은 case 에서 backend selection_path=rank_1 (env toggle 통과) // 이어도 current_default=None 이면 default 와 override mismatch 로 preview 강제 발동. zone.internal_regions.forEach(region => { const topFrameId = region.frame_match_strategy.frame_id; if (topFrameId) { initialFrames[region.id] = topFrameId; } }); }); } return { selectedSectionId: null, selectedZoneId: slidePlan?.zones[0]?.id || null, selectedRegionId: slidePlan?.zones[0]?.internal_regions[0]?.id || null, overrides: { layout_preset: slidePlan?.layout_preset, zone_frames: initialFrames, zone_sections: initialSections, zone_sizes: {}, zone_geometries: {}, }, }; } export function saveZoneGeometry( selection: UserSelection, zoneId: string, geometry: { x: number; y: number; w: number; h: number } ): UserSelection { return { ...selection, overrides: { ...selection.overrides, zone_geometries: { ...selection.overrides.zone_geometries, [zoneId]: geometry, } } }; } export function saveZoneSizes(selection: UserSelection, groupId: string, sizes: number[]): UserSelection { return { ...selection, overrides: { ...selection.overrides, zone_sizes: { ...selection.overrides.zone_sizes, [groupId]: sizes, } } }; } /** * 특정 섹션을 새로운 존으로 이동 (Drag & Drop) */ export function moveSectionToZone( selection: UserSelection, sectionId: string, targetZoneId: string ): UserSelection { const newZoneSections = { ...selection.overrides.zone_sections }; // 1. 모든 존에서 해당 섹션 제거 (이동 전 위치 클리어) Object.keys(newZoneSections).forEach(zid => { newZoneSections[zid] = newZoneSections[zid].filter(id => id !== sectionId); }); // 2. 타겟 존에 섹션 추가 if (!newZoneSections[targetZoneId]) { newZoneSections[targetZoneId] = []; } if (!newZoneSections[targetZoneId].includes(sectionId)) { newZoneSections[targetZoneId].push(sectionId); } return { ...selection, overrides: { ...selection.overrides, zone_sections: newZoneSections } }; } export function selectZone(selection: UserSelection, zoneId: string | null): UserSelection { return { ...selection, selectedZoneId: zoneId, selectedRegionId: null, // Zone이 바뀌면 Region 선택 해제 }; } export function selectRegion(selection: UserSelection, regionId: string | null): UserSelection { return { ...selection, selectedRegionId: regionId, }; } export function applyLayout(selection: UserSelection, layoutId: LayoutPresetId): UserSelection { return { ...selection, overrides: { ...selection.overrides, layout_preset: layoutId, }, }; } export function applyFrame(selection: UserSelection, regionId: string, frameId: string): UserSelection { return { ...selection, overrides: { ...selection.overrides, zone_frames: { ...selection.overrides.zone_frames, [regionId]: frameId, }, }, }; } /** * 현재 선택된 Zone 객체 반환 */ export function getSelectedZone(slidePlan: SlidePlan | null, selection: UserSelection): Zone | null { if (!slidePlan || !selection.selectedZoneId) return null; // id 또는 zone_id 매칭 return slidePlan.zones.find(z => z.id === selection.selectedZoneId || z.zone_id === selection.selectedZoneId) || null; } /** * 현재 선택된 Region 객체 반환 */ export function getSelectedRegion(zone: Zone | null, selection: UserSelection): InternalRegion | null { if (!zone || !selection.selectedRegionId) return null; return zone.internal_regions.find(r => r.id === selection.selectedRegionId) || null; } /** * 특정 Zone에 할당된 섹션 ID 목록 반환 (오버라이드 우선) */ export function getSectionsForZone(zone: Zone, selection: UserSelection): string[] { return selection.overrides.zone_sections[zone.zone_id] || zone.section_ids; } /** * 최종 유효 레이아웃 ID 반환 */ export function getEffectiveLayoutId(slidePlan: SlidePlan | null, selection: UserSelection): LayoutPresetId { if (selection.overrides.layout_preset) return selection.overrides.layout_preset; return slidePlan?.layout_preset || 'single'; }