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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 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 Formatting Utilities
*
* Functions for formatting flowchart problem values for display.
*
* @module flowcharts/formatting
*/
import type { ExecutableFlowchart, ProblemValue, MixedNumberValue } from './schema'
import { evaluate, type EvalContext } from './evaluator'
// =============================================================================
// Template Interpolation
// =============================================================================
/**
* Interpolate a template string with values.
*
* Supports two syntaxes:
* - `{{name}}` - Simple variable substitution from values
* - `{{=expr}}` - Expression evaluation using the evaluator
*
* @example
* ```ts
* interpolateTemplate("x = {{answer}}", { answer: 5 })
* // Returns: "x = 5"
*
* interpolateTemplate("Sum: {{=a + b}}", { a: 2, b: 3 })
* // Returns: "Sum: 5"
* ```
*/
export function interpolateTemplate(
template: string,
values: Record<string, ProblemValue>
): string {
// Match {{...}} patterns
return template.replace(/\{\{([^}]+)\}\}/g, (match, content: string) => {
const trimmed = content.trim()
// Check if it's an expression (starts with =)
if (trimmed.startsWith('=')) {
const expr = trimmed.slice(1).trim()
try {
const context: EvalContext = {
problem: values,
computed: values, // In new model, values contains everything
userState: {},
}
const result = evaluate(expr, context)
return formatValueForDisplay(result)
} catch (error) {
console.error(`Template expression error: ${expr}`, error)
return match // Return original on error
}
}
// Simple variable substitution
if (trimmed in values) {
return formatValueForDisplay(values[trimmed])
}
// Variable not found - return original
console.warn(`Template variable not found: ${trimmed}`)
return match
})
}
/**
* Format a ProblemValue for display in templates.
*/
function formatValueForDisplay(value: ProblemValue): string {
if (value === null || value === undefined) {
return ''
}
if (typeof value === 'object' && 'denom' in value) {
return formatMixedNumber(value as MixedNumberValue)
}
return String(value)
}
// =============================================================================
// Mixed Number Helpers
// =============================================================================
/**
* Create a mixed number value
*/
export function createMixedNumber(whole: number, num: number, denom: number): MixedNumberValue {
return { whole, num, denom }
}
/**
* Format a mixed number for display
*/
export function formatMixedNumber(mn: MixedNumberValue): string {
if (mn.whole === 0) {
return `${mn.num}/${mn.denom}`
}
if (mn.num === 0) {
return String(mn.whole)
}
return `${mn.whole} ${mn.num}/${mn.denom}`
}
// =============================================================================
// Problem Display Formatting
// =============================================================================
/**
* Format problem input for display.
*
* If flowchart has `display.problem` expression, evaluates it.
* Otherwise falls back to showing problem values joined together.
*/
export function formatProblemDisplay(
flowchart: ExecutableFlowchart,
problem: Record<string, ProblemValue>
): string {
// If the flowchart has a display.problem expression, evaluate it
if (flowchart.definition.display?.problem) {
try {
const context: EvalContext = {
problem,
computed: {},
userState: {},
}
const result = evaluate(flowchart.definition.display.problem, context)
return String(result)
} catch (error) {
console.error(
`Error evaluating display.problem for flowchart "${flowchart.definition.id}":`,
error
)
// Fall through to fallback
}
}
// Fallback: show problem values in a basic format
return Object.entries(problem)
.map(([, v]) => {
if (typeof v === 'object' && v !== null && 'denom' in v) {
return formatMixedNumber(v as MixedNumberValue)
}
return String(v)
})
.join(' ')
}
|