Generate new calculator screens from configuration
Generate fully functional calculator screens for the SourdoughSuite app from a minimal configuration file.
Reduces calculator boilerplate from 700 lines to ~50 lines of config. Generates screens that:
sourdoughCalculations utilitiesInvoke with:
/calculator-generator <config-file-path>
Or provide the config inline:
/calculator-generator
Then describe the calculator you want to create.
Create a JSON config file with this structure:
{
"name": "Autolyse Calculator",
"fileName": "AutolyseCalculatorScreen",
"icon": "water-outline",
"description": "Calculate autolyse timing and hydration",
"subtitle": "Optimize your autolyse phase",
"inputs": [
{
"name": "flourWeight",
"label": "Flour Weight (g)",
"placeholder": "e.g., 500",
"keyboardType": "numeric",
"required": true,
"leftIcon": "bread-slice"
},
{
"name": "autolyseDuration",
"label": "Autolyse Duration (minutes)",
"placeholder": "e.g., 30",
"keyboardType": "numeric",
"required": true
}
],
"calculation": {
"formula": "autolyseWater = calculateWaterNeeded(flourWeight, 75)",
"imports": ["calculateWaterNeeded"],
"description": "Calculates 75% hydration for autolyse"
},
"results": [
{
"label": "Flour",
"value": "flourWeight",
"unit": "g"
},
{
"label": "Water for Autolyse",
"value": "autolyseWater",
"unit": "g"
},
{
"label": "Rest Duration",
"value": "autolyseDuration",
"unit": "minutes"
}
],
"infoCard": {
"title": "What is Autolyse?",
"content": "Autolyse is a resting period where flour and water are mixed before adding salt and starter. This allows enzymes to break down flour proteins, improving dough extensibility and flavor.\\n\\n• Typical duration: 30-60 minutes\\n• Uses 70-80% of total water\\n• Improves gluten development"
},
"saveAsRecipe": true
}
When invoked, this skill will:
src/screens/tools/{FileName}.tsx)
src/navigation/types.ts)
src/navigation/ToolsNavigator.tsx)
src/screens/tools/ToolsListScreen.tsx)
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
KeyboardAvoidingView,
Platform,
Alert,
} from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import Button from '../../components/Button';
import BasicInput from '../../components/BasicInput';
import Card from '../../components/Card';
import { theme } from '../../theme';
import { ToolsStackParamList } from '../../navigation/types';
import { calculateWaterNeeded, roundTo } from '../../utils/sourdoughCalculations';
type Props = NativeStackScreenProps<ToolsStackParamList, 'CalculatorName'>;
export default function CalculatorNameScreen({ navigation }: Props) {
// State for input fields
const [flourWeight, setFlourWeight] = useState('');
const [autolyseDuration, setAutolyseDuration] = useState('');
// State for results
const [showResults, setShowResults] = useState(false);
const [results, setResults] = useState<{
flourWeight: string;
autolyseWater: string;
autolyseDuration: string;
} | null>(null);
const handleCalculate = () => {
// Validation
if (!flourWeight.trim() || !autolyseDuration.trim()) {
Alert.alert('Validation Error', 'All fields are required');
return;
}
// Parse inputs
const flourWeightNum = parseFloat(flourWeight);
const autolyseDurationNum = parseFloat(autolyseDuration);
// Validation
if (isNaN(flourWeightNum) || flourWeightNum <= 0) {
Alert.alert('Validation Error', 'Flour weight must be a positive number');
return;
}
// Calculation
const autolyseWater = roundTo(calculateWaterNeeded(flourWeightNum, 75), 0);
// Set results
setResults({
flourWeight: flourWeightNum.toFixed(0),
autolyseWater: autolyseWater.toFixed(0),
autolyseDuration: autolyseDurationNum.toFixed(0),
});
setShowResults(true);
};
const handleClear = () => {
setFlourWeight('');
setAutolyseDuration('');
setShowResults(false);
setResults(null);
};
const handleSaveAsRecipe = () => {
if (!results) return;
navigation.navigate('AddRecipe', {
prefillData: {
name: 'Autolyse Mix',
ingredients: [
{ name: 'Flour', amount: results.flourWeight, unit: 'g' },
{ name: 'Water', amount: results.autolyseWater, unit: 'g' },
],
},
});
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}>
<ScrollView style={styles.content}>
{/* Header */}
<View style={styles.header}>
<Icon name="water-outline" size={48} color={theme.colors.primary} />
<Text style={styles.title}>Autolyse Calculator</Text>
<Text style={styles.subtitle}>Optimize your autolyse phase</Text>
</View>
{/* Input Fields */}
<Card style={styles.card}>
<BasicInput
label="Flour Weight (g)"
value={flourWeight}
onChangeText={setFlourWeight}
placeholder="e.g., 500"
keyboardType="numeric"
leftIcon="bread-slice"
/>
<BasicInput
label="Autolyse Duration (minutes)"
value={autolyseDuration}
onChangeText={setAutolyseDuration}
placeholder="e.g., 30"
keyboardType="numeric"
/>
</Card>
{/* Results Card */}
{showResults && results && (
<Card style={styles.card}>
<Text style={styles.sectionTitle}>Autolyse Recipe</Text>
<View style={styles.resultRow}>
<Text style={styles.resultLabel}>Flour</Text>
<Text style={styles.resultValue}>{results.flourWeight}g</Text>
</View>
<View style={styles.resultRow}>
<Text style={styles.resultLabel}>Water for Autolyse</Text>
<Text style={styles.resultValue}>{results.autolyseWater}g</Text>
</View>
<View style={styles.resultRow}>
<Text style={styles.resultLabel}>Rest Duration</Text>
<Text style={styles.resultValue}>{results.autolyseDuration} minutes</Text>
</View>
</Card>
)}
{/* Action Buttons */}
<View style={styles.actions}>
{!showResults ? (
<Button title="Calculate" onPress={handleCalculate} />
) : (
<>
<Button
title="Save as Recipe"
onPress={handleSaveAsRecipe}
variant="outline"
/>
<Button title="Clear" onPress={handleClear} variant="outline" />
<Button title="Recalculate" onPress={handleCalculate} />
</>
)}
</View>
{/* Info Card */}
<Card style={styles.infoCard}>
<Text style={styles.infoTitle}>What is Autolyse?</Text>
<Text style={styles.infoText}>
Autolyse is a resting period where flour and water are mixed before adding salt and starter...
</Text>
</Card>
</ScrollView>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
// Standard styles using theme constants
});
src/screens/tools/{FileName}.tsxsrc/navigation/types.ts - Add screen to ToolsStackParamListsrc/navigation/ToolsNavigator.tsx - Register screensrc/screens/tools/ToolsListScreen.tsx - Add to tools listRun these checks:
# Type check
npx tsc
# Test in app
npm run android
# or
npm run ios
Verify:
{
"name": "Salt Calculator",
"fileName": "SaltCalculatorScreen",
"icon": "shaker-outline",
"description": "Calculate salt amount for dough",
"subtitle": "2% is standard",
"inputs": [
{
"name": "flourWeight",
"label": "Flour Weight (g)",
"placeholder": "e.g., 1000",
"keyboardType": "numeric",
"required": true
},
{
"name": "saltPercent",
"label": "Salt Percentage",
"placeholder": "e.g., 2",
"keyboardType": "numeric",
"required": true
}
],
"calculation": {
"formula": "saltAmount = roundTo(calculateAmountFromPercentage(flourWeight, saltPercent), 1)",
"imports": ["calculateAmountFromPercentage", "roundTo"]
},
"results": [
{
"label": "Salt Amount",
"value": "saltAmount",
"unit": "g"
}
],
"infoCard": {
"title": "Salt in Sourdough",
"content": "Salt strengthens gluten, controls fermentation, and enhances flavor. Standard range is 1.8-2.2% of flour weight."
}
}
{
"name": "Dough Temperature Calculator",
"fileName": "DoughTempCalculatorScreen",
"icon": "thermometer",
"description": "Calculate water temperature for desired dough temp",
"inputs": [
{
"name": "targetDDT",
"label": "Target Dough Temperature (°F)",
"placeholder": "e.g., 78",
"keyboardType": "numeric",
"required": true
},
{
"name": "roomTemp",
"label": "Room Temperature (°F)",
"placeholder": "e.g., 70",
"keyboardType": "numeric",
"required": true
},
{
"name": "flourTemp",
"label": "Flour Temperature (°F)",
"placeholder": "e.g., 68",
"keyboardType": "numeric",
"required": true
},
{
"name": "starterTemp",
"label": "Starter Temperature (°F)",
"placeholder": "e.g., 72",
"keyboardType": "numeric",
"required": true
}
],
"calculation": {
"formula": "waterTemp = calculateWaterTemperature(targetDDT, roomTemp, flourTemp, starterTemp, 5)",
"imports": ["calculateWaterTemperature"]
},
"results": [
{
"label": "Water Temperature",
"value": "waterTemp",
"unit": "°F"
}
],
"infoCard": {
"title": "Desired Dough Temperature (DDT)",
"content": "DDT controls fermentation speed. 75-78°F is ideal for most sourdough recipes. Warmer = faster fermentation."
}
}
Study these existing calculators for patterns:
src/screens/tools/BakersCalculatorScreen.tsx - Comprehensive examplesrc/screens/tools/HydrationCalculatorScreen.tsx - Simple percentage calcsrc/screens/tools/LevainCalculatorScreen.tsx - Multi-step calculation