import type { InterviewProfile, InterviewStep } from "./types"; import type { LoopRuntimeState } from "./loopState"; export interface PendingEdgeAssignment { filePath: string; sectionKey: string; // identifies heading/section in file (e.g. "H2:Wendepunkte..." or "ROOT") linkBasename: string; // target note basename chosenRawType: string; // user chosen edge type (alias allowed) sourceNoteId?: string; // from frontmatter.id (optional) targetNoteId?: string; // if resolved (optional) createdAt: number; } export interface WizardState { profile: InterviewProfile; currentStepIndex: number; stepHistory: number[]; // Stack for back navigation collectedData: Map; // key -> value loopContexts: Map; // loop key -> array of collected items (deprecated, use loopRuntimeStates) loopRuntimeStates: Map; // loop step key -> runtime state patches: Patch[]; // Collected patches to apply activeLoopPath: string[]; // Stack of loop keys representing current nesting level (e.g. ["items", "item_list"]) pendingEdgeAssignments: PendingEdgeAssignment[]; // Inline micro edge assignments collected during wizard } export interface Patch { type: "frontmatter" | "content"; field?: string; // For frontmatter patches value: unknown; lineStart?: number; lineEnd?: number; } export function createWizardState(profile: InterviewProfile): WizardState { // Log flattened steps before creating state const flat = flattenSteps(profile.steps); const flatKinds = flat.map(s => s.type); console.log("Wizard flattened steps", { count: flat.length, kinds: flatKinds, }); return { profile, currentStepIndex: 0, stepHistory: [], collectedData: new Map(), loopContexts: new Map(), // Keep for backwards compatibility loopRuntimeStates: new Map(), patches: [], activeLoopPath: [], // Start at top level pendingEdgeAssignments: [], // Start with empty pending assignments // WP-26: Initialize Section-Type und Block-ID Tracking generatedBlockIds: new Map(), sectionSequence: [], }; } export function getCurrentStep(state: WizardState): InterviewStep | null { const steps = flattenSteps(state.profile.steps); // Log flattened steps count if (state.currentStepIndex === 0) { console.log("Flattened steps", { count: steps.length }); } if (steps.length === 0) { console.warn("No steps available in profile", { profileKey: state.profile.key }); return null; } if (state.currentStepIndex >= 0 && state.currentStepIndex < steps.length) { return steps[state.currentStepIndex] || null; } return null; } export function getNextStepIndex(state: WizardState): number | null { const steps = flattenSteps(state.profile.steps); if (state.currentStepIndex < steps.length - 1) { return state.currentStepIndex + 1; } return null; } export function getPreviousStepIndex(state: WizardState): number | null { if (state.stepHistory.length > 0) { const prev = state.stepHistory[state.stepHistory.length - 1]; return prev !== undefined ? prev : null; } return null; } export function canGoNext(state: WizardState): boolean { return getNextStepIndex(state) !== null; } export function canGoBack(state: WizardState): boolean { return state.stepHistory.length > 0; } /** * Flatten steps including loops into a linear array. * Returns ALL top-level steps (instruction, capture_text, capture_frontmatter, llm_dialog, review, loop). * For loops, includes the loop step itself (nested items are handled during execution). * Exported for use in InterviewWizardModal. */ export function flattenSteps(steps: InterviewStep[]): InterviewStep[] { const result: InterviewStep[] = []; if (!steps || steps.length === 0) { return result; } for (const step of steps) { if (!step) { console.warn("Skipping null/undefined step"); continue; } // Include all step types: instruction, capture_text, capture_frontmatter, llm_dialog, review, loop // For loops, include the loop step itself (nested items handled during execution) result.push(step); } return result; }