Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 192x 192x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | /**
* Flowchart Walker Schema Types
*
* This module defines all TypeScript types for the flowchart walker system.
* These types correspond to the structure of `.flow.json` files and the
* runtime state used during flowchart execution.
*
* ## Architecture Overview
*
* ```
* FlowchartDefinition (from .flow.json)
* ├── problemInput: ProblemInputSchema # User input form definition
* ├── nodes: Record<string, FlowchartNode> # Node behavior definitions
* │ └── transform?: TransformExpression[] # Computations at this node
* ├── answer?: AnswerDefinition # How to extract final answer
* ├── workingProblem?: WorkingProblemConfig # Evolving problem display
* └── constraints?: GenerationConstraints # Problem generation rules
*
* ParsedMermaid (from .mmd or embedded)
* ├── nodes: Record<string, string> # Raw node content
* ├── edges: ParsedEdge[] # Connections between nodes
* └── phases: Phase[] # Subgraph groupings
*
* ExecutableFlowchart = FlowchartDefinition + ParsedMermaid merged
* └── nodes: Record<string, ExecutableNode> # Ready for display
*
* FlowchartState (runtime)
* ├── problem: user input values
* ├── values: accumulated transform results (starts with problem)
* ├── currentNode: where we are
* ├── snapshots: state history at each node (for trace)
* └── history: actions taken
* ```
*
* ## Computation Model
*
* Transforms execute progressively during the walk:
* 1. State starts with problem input in `values`
* 2. Each node can have `transform` array
* 3. Transforms execute in order, results accumulate in `values`
* 4. Later transforms can reference earlier ones
* 5. Terminal state contains final answer in `values`
* 6. `extractAnswer()` uses `answer` definition to format the result
*
* ## Node Types
*
* | Type | Purpose | User Action |
* |------|---------|-------------|
* | `instruction` | Show content | Tap to continue |
* | `decision` | Yes/No or multiple choice | Tap option |
* | `checkpoint` | Validate user answer | Enter value |
* | `milestone` | Success marker | Auto-advances |
* | `embellishment` | Decorative emoji/celebration | Auto-advances with animation |
* | `terminal` | End state | Shows completion |
*
* ## File Locations
*
* - JSON definitions: `lib/flowcharts/definitions/*.flow.json`
* - Mermaid content: `lib/flowcharts/definitions/index.ts` (embedded) or `*.mmd`
* - This file: Type definitions only, no runtime logic
*
* @see {@link ../README.md} for complete system documentation
* @module flowcharts/schema
*/
// =============================================================================
// Problem Input Schemas
// =============================================================================
/** Field types for problem input forms */
export type FieldType = 'integer' | 'number' | 'choice' | 'mixed-number' | 'dynamic'
/** Base field definition */
export interface BaseField {
name: string
label?: string
type: FieldType
}
/** Integer field with optional constraints */
export interface IntegerField extends BaseField {
type: 'integer'
min?: number
max?: number
default?: number
}
/** Number field (allows decimals) */
export interface NumberField extends BaseField {
type: 'number'
min?: number
max?: number
default?: number
step?: number
}
/** Choice field (dropdown/select) */
export interface ChoiceField extends BaseField {
type: 'choice'
options: string[]
default?: string
}
/** Mixed number field (whole + numerator + denominator) */
export interface MixedNumberField extends BaseField {
type: 'mixed-number'
}
/** Dynamic field (changes based on other field values) */
export interface DynamicField extends BaseField {
type: 'dynamic'
dependsOn: string
variants: Record<string, Field[]>
}
export type Field = IntegerField | NumberField | ChoiceField | MixedNumberField | DynamicField
/** An example problem that can be pre-loaded */
export interface ProblemExample {
/** Display name for the example (e.g., "No regrouping") */
name: string
/** Description of the path this covers */
description?: string
/** Values to populate the form with */
values: Record<string, ProblemValue>
/** Expected answer string that display.answer should produce (for test validation) */
expectedAnswer?: string
/** Optional: expected decision path values taken (e.g., ["direct", "same"]) */
expectedPath?: string[]
}
/** Problem input schema definition */
export interface ProblemInputSchema {
schema: string
fields: Field[]
/** Optional expression that must evaluate to true for input to be valid */
validation?: string
/** Pre-defined example problems that cover different paths */
examples?: ProblemExample[]
}
// =============================================================================
// Transforms (progressive computation during walk)
// =============================================================================
/**
* A single transform expression that computes a value.
* Transforms execute in order - later transforms can reference earlier ones.
*/
export interface TransformExpression {
/** Variable name to create/update in accumulated state */
key: string
/** Expression to evaluate (uses existing evaluator syntax) */
expr: string
}
/**
* Snapshot of state at a specific node (for trace visualization).
* One snapshot is recorded per node visited during a walk.
*/
export interface StateSnapshot {
/** Node ID where this snapshot was taken */
nodeId: string
/** Human-readable node title for display */
nodeTitle?: string
/** Accumulated values after transforms at this node */
values: Record<string, ProblemValue>
/** Transforms that were applied at this node */
transforms: TransformExpression[]
/** Working problem display after this node (if applicable) */
workingProblem?: string
/** Timestamp when this snapshot was taken */
timestamp: number
/** For decision nodes: the option value that was selected (e.g., "MD" for multiply/divide) */
selectedOptionValue?: string
/** For decision nodes: the next node ID that was navigated to */
nextNodeId?: string
/**
* The unique ID of the edge that was taken (from ParsedEdge.id).
* When edges use the mermaid `id@-->` syntax, this ID appears directly
* in the SVG element, enabling reliable ID-based matching.
*/
edgeId?: string
/**
* The index of the edge in parse order (from ParsedEdge.index).
* Used as fallback for matching when edge IDs don't use `id@-->` syntax.
*/
edgeIndex?: number
}
// =============================================================================
// Answer Definition (extracted from terminal state)
// =============================================================================
/**
* Template for displaying answers in different contexts.
*/
export interface DisplayTemplate {
/** Plain text format (for tests, simple display) */
text: string
/** Web format with MathML (for browser display) */
web?: string
/** Typst format (for PDF worksheets) */
typst?: string
}
/**
* Defines how to extract the final answer from terminal state.
*/
export interface AnswerDefinition {
/**
* Map answer component names to variable refs in accumulated state.
* Example: { "answer": "finalResult" } extracts state.values.finalResult
*/
values: Record<string, string>
/** Templates for displaying the answer */
display: DisplayTemplate
}
// =============================================================================
// Structured Test Cases
// =============================================================================
/**
* A test case with structured expected values (primitives, not strings).
* Used for validating flowchart correctness.
*/
export interface StructuredTestCase {
/** Human-readable test name */
name: string
/** Problem input values */
values: Record<string, ProblemValue>
/** Expected answer values (primitives, compared directly) */
expected: Record<string, ProblemValue>
}
// =============================================================================
// Working Problem (evolving problem display)
// =============================================================================
/**
* Configuration for the working problem feature.
* The working problem shows the problem as it evolves through the solution process.
* Example: "2x - 9 = 3" → "2x = 12" → "x = 6"
*/
export interface WorkingProblemConfig {
/** Expression that evaluates to the initial working problem display string */
initial: string
}
/**
* Defines how a node transforms the working problem.
* Applied when the user successfully completes the node.
*/
export interface WorkingProblemStep {
/** Expression that evaluates to the new working problem representation */
result: string
/** Label describing what operation was performed (e.g., "Add 9 to both sides") */
label: string
}
// =============================================================================
// Node Types
// =============================================================================
/** Base node properties shared by all node types */
interface BaseNode {
/** Optional explicit next node (for nodes with single outgoing edge) */
next?: string
/**
* Transforms to apply when entering this node.
* Executed in order - later transforms can reference earlier ones.
* Results accumulate in state.values.
*/
transform?: TransformExpression[]
}
/**
* Instruction node - shows content, waits for "I did it" tap
*/
export interface InstructionNode extends BaseNode {
type: 'instruction'
advance: 'tap'
/** Optional: transform the working problem when this node is completed */
workingProblemUpdate?: WorkingProblemStep
}
/**
* Decision node - shows question, user taps choice, routes accordingly
*/
export interface DecisionNode extends BaseNode {
type: 'decision'
/** Expression that evaluates to the correct answer (true/false or matches option value) */
correctAnswer?: string
/** Available choices */
options: DecisionOption[]
/** Optional: skip this decision entirely if expression is true */
skipIf?: string
/** Node to skip to if skipIf evaluates to true */
skipTo?: string
/**
* If true, this decision is excluded from consideration when structuring examples.
* Use for pedagogical decisions that shouldn't define grid dimensions.
*/
excludeFromExampleStructure?: boolean
}
/** A single option in a decision node */
export interface DecisionOption {
/** Display label for the button */
label: string
/** Value used for routing and validation */
value: string
/** Next node if this option is selected */
next: string
/** Short label for path descriptors (e.g., "Same", "+"). Used in concatenated paths like "Same +" */
pathLabel?: string
/** Kid-friendly label for grid headers (e.g., "Same denominators", "Addition"). Falls back to pathLabel if not provided */
gridLabel?: string
}
/**
* Compute the expected mermaid edge ID for a decision option.
* Pattern: {nodeId}_{optionValue}
*
* Mermaid content must use this exact ID with the `id@-->` syntax:
* COMPARE COMPARE_direct@-->|"DIRECT"| HAPPY
*
* @param nodeId - The decision node ID
* @param optionValue - The option's value field
* @returns The expected edge ID
*/
export function computeEdgeId(nodeId: string, optionValue: string): string {
return `${nodeId}_${optionValue}`
}
/**
* Checkpoint node - user enters a value, app validates
*/
export interface CheckpointNode extends BaseNode {
type: 'checkpoint'
/** Prompt text shown to user */
prompt: string
/** Type of input expected */
inputType: 'number' | 'text' | 'two-numbers'
/** Expression that evaluates to the expected value (or array of expressions for two-numbers) */
expected: string | [string, string]
/** Labels for two-numbers inputs (defaults to ["First", "Second"]) */
inputLabels?: [string, string]
/** For two-numbers: whether order matters (default: true). Set to false if either order is acceptable. */
orderMatters?: boolean
/** Optional state update when checkpoint is passed */
stateUpdate?: Record<string, string>
/** Optional tolerance for numeric comparisons (for decimals) */
tolerance?: number
/** Optional: transform the working problem when this checkpoint is passed */
workingProblemUpdate?: WorkingProblemStep
/** Optional: skip this checkpoint entirely if expression is true */
skipIf?: string
/** Node to skip to if skipIf evaluates to true */
skipTo?: string
/**
* If true, the skipIf condition won't create a branching path for grid dimensions.
* Use for optional steps that don't represent different problem types.
* Default: false (skipIf creates a branch for path enumeration)
*/
excludeSkipFromPaths?: boolean
}
/**
* Milestone node - intermediate success marker, auto-advances
*/
export interface MilestoneNode extends BaseNode {
type: 'milestone'
/** Required: next node to advance to */
next: string
}
/**
* Embellishment node - decorative emoji/celebration moment
* Shows an animated emoji within the content card, then auto-advances.
* Used for encouraging moments like 👍, 😎, 💪 between substantive steps.
*/
export interface EmbellishmentNode extends BaseNode {
type: 'embellishment'
/** Required: next node to advance to */
next: string
}
/**
* Terminal node - end state
*/
export interface TerminalNode extends BaseNode {
type: 'terminal'
/** Show celebration animation */
celebration?: boolean
}
export type FlowchartNode =
| InstructionNode
| DecisionNode
| CheckpointNode
| MilestoneNode
| EmbellishmentNode
| TerminalNode
// =============================================================================
// Generation Configuration
// =============================================================================
/**
* Preferred values for a field during generation.
* Can be an array of specific values or a range configuration.
*/
export type PreferredValues =
| number[]
| string[]
| {
range: [number, number]
step?: number
}
/**
* Configuration for constraint-guided example generation.
* This allows the flowchart to express how to generate pedagogically
* appropriate problems without schema-specific code in the engine.
*/
export interface GenerationConfig {
/**
* The "target" field - typically the answer we're solving for.
* Generated first with nice values, then other fields are derived/generated.
* Should reference a variable name from the variables section.
*/
target?: string
/**
* Fields that are computed from other fields rather than generated randomly.
* Maps field name to expression that computes it.
* Example: { "equals": "coefficient * answer + constant" }
*/
derived?: Record<string, string>
/**
* Preferred values for fields - pedagogically nice numbers.
* The engine will bias generation toward these values.
*/
preferred?: Record<string, PreferredValues>
}
/**
* Teacher/flowchart-defined constraints that generated problems must satisfy.
* Each constraint is an expression that must evaluate to true.
*/
export type GenerationConstraints = Record<string, string>
/**
* Configuration for how to display the problem.
*/
export interface DisplayConfig {
/**
* Expression that evaluates to the problem display string.
* Example: "coefficient + 'x ' + operation + ' ' + constant + ' = ' + equals"
*/
problem?: string
/**
* Expression that evaluates to the answer display string.
* Example: "'x = ' + answer" or "answer" or complex fraction formatting
*/
answer?: string
}
// =============================================================================
// Flowchart Definition
// =============================================================================
/**
* @deprecated Use transform on nodes instead
* Variable definition with initialization expression (legacy)
*/
export interface VariableDefinition {
/** Expression to evaluate at start to initialize this variable */
init: string
}
/**
* Complete flowchart definition (.flow.json)
*/
export interface FlowchartDefinition {
/** Unique identifier for this flowchart */
id: string
/** Human-readable title */
title: string
/** Path to the companion .mmd file */
mermaidFile: string
/** Problem input configuration */
problemInput: ProblemInputSchema
/**
* @deprecated Use transform on nodes instead.
* Variables computed at start - being replaced by node transforms.
*/
variables?: Record<string, VariableDefinition>
/** First node to display */
entryNode: string
/** Node definitions keyed by node ID */
nodes: Record<string, FlowchartNode>
/**
* Fallback edge definitions (used when node doesn't specify next)
* Maps node ID to array of possible next nodes
*/
edges?: Record<string, string[]>
/**
* Optional: Configuration for the working problem feature.
* When defined, displays an evolving problem representation as the user progresses.
*/
workingProblem?: WorkingProblemConfig
/**
* Optional: Configuration for constraint-guided example generation.
* Defines how to generate pedagogically appropriate problems.
*/
generation?: GenerationConfig
/**
* Optional: Constraints that generated problems must satisfy.
* Each key is a constraint name, value is an expression that must be true.
* Example: { "positiveAnswer": "answer > 0", "integerAnswer": "floor(answer) == answer" }
*/
constraints?: GenerationConstraints
/**
* Optional: Configuration for how to display the problem (legacy).
* Being replaced by answer.display.
*/
display?: DisplayConfig
/**
* Defines how to extract the final answer from terminal state.
* Required for new-format flowcharts using transforms.
*/
answer?: AnswerDefinition
/**
* Structured test cases for validating flowchart correctness.
* Uses primitive expected values instead of string matching.
*/
tests?: StructuredTestCase[]
}
// =============================================================================
// Runtime State
// =============================================================================
/** Structured mixed number value */
export interface MixedNumberValue {
whole: number
num: number
denom: number
}
/** Problem input values (from user form) */
export type ProblemValue = number | string | boolean | MixedNumberValue
/** History entry for tracking user actions */
export interface HistoryEntry {
node: string
action: 'advance' | 'decision' | 'checkpoint' | 'skip'
timestamp: number
userInput?: ProblemValue
correct?: boolean
}
/** A recorded step in the working problem evolution */
export interface WorkingProblemHistoryEntry {
/** The working problem representation after this step */
value: string
/** Label describing what was done (e.g., "Add 9 to both sides") */
label: string
/** Node ID where this transformation occurred */
nodeId: string
}
/**
* Runtime state for a flowchart walkthrough
*/
export interface FlowchartState {
/** Problem input values (immutable after start) */
problem: Record<string, ProblemValue>
/**
* @deprecated Use values instead.
* Computed values from variable init expressions (legacy).
*/
computed: Record<string, ProblemValue>
/**
* Accumulated values from transforms (starts with problem input).
* This is the new way - transforms add to this as nodes are visited.
*/
values: Record<string, ProblemValue>
/** User-entered values during walkthrough */
userState: Record<string, ProblemValue>
/** Current node ID */
currentNode: string
/** History of actions taken */
history: HistoryEntry[]
/** Session start time */
startTime: number
/** Number of wrong answers */
mistakes: number
/** Current working problem display string (evolves through walkthrough) */
workingProblem?: string
/** History of working problem transformations */
workingProblemHistory: WorkingProblemHistoryEntry[]
/**
* Snapshots of state at each node visited (for trace visualization).
* Each entry represents the state after visiting that node.
*/
snapshots: StateSnapshot[]
/**
* Whether any transform errors occurred during the walk.
* Errors are logged but don't stop the walk - values are set to null.
*/
hasError: boolean
}
// =============================================================================
// Parsed Mermaid Content
// =============================================================================
/** Parsed content from a Mermaid node label */
export interface ParsedNodeContent {
/** Title text (from first <b>...</b>) */
title: string
/** Main body lines */
body: string[]
/** Example text (lines with emoji or <i>) */
example?: string
/** Warning text (lines with warning emoji) */
warning?: string
/** Checklist items (lines starting with checkbox) */
checklist?: string[]
/** Raw content for fallback display */
raw: string
}
/** Edge parsed from Mermaid file */
export interface ParsedEdge {
from: string
to: string
label?: string
/**
* Unique edge identifier. Can come from:
* 1. Mermaid edge ID syntax (e.g., `myId@-->`)
* 2. Generated during parsing as `edge_${globalIndex}`
*
* This ID is used for reliable edge matching in visualizations.
*/
id: string
/**
* Global index of this edge in parse order (0-based).
* Mermaid renders edges in this order, so SVG edge elements
* correspond to this index.
*/
index: number
}
/** Parsed Mermaid flowchart structure */
export interface ParsedMermaid {
/** Node ID to raw content mapping */
nodes: Record<string, string>
/** All edges in the flowchart */
edges: ParsedEdge[]
/** Subgraph (phase) definitions */
phases: Array<{
id: string
title: string
nodes: string[]
}>
}
// =============================================================================
// Merged Flowchart (ready for execution)
// =============================================================================
/** Node ready for display with both definition and parsed content */
export interface ExecutableNode {
id: string
definition: FlowchartNode
content: ParsedNodeContent
}
/** Complete flowchart ready for execution */
export interface ExecutableFlowchart {
definition: FlowchartDefinition
mermaid: ParsedMermaid
/** Raw mermaid content string (for debug rendering) */
rawMermaid: string
nodes: Record<string, ExecutableNode>
}
|