This commit is contained in:
2026-03-03 16:43:30 +00:00
commit 03452517b5
58 changed files with 13181 additions and 0 deletions

173
src/pages/Solver.jsx Normal file
View File

@@ -0,0 +1,173 @@
import { DndContext, DragOverlay, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'
import { useState } from 'react'
import { VariablePalette } from '../components/VariablePalette.jsx'
import { Workspace } from '../components/Workspace.jsx'
import { ResultsPanel } from '../components/ResultsPanel.jsx'
import { PaletteCard } from '../components/VariableCard.jsx'
import { PropellantModal } from '../components/PropellantModal.jsx'
import { EquationBrowser } from '../components/EquationBrowser.jsx'
import { useSolver } from '../hooks/useSolver.js'
import { EQUATIONS } from '../engine/equations.js'
import { buildExportData, exportJSON, parseImport, downloadBlob } from '../engine/exportImport.js'
import { generateOdt } from '../engine/exportOdt.js'
export default function Solver() {
const {
workspaceVarIds,
userValues,
solved,
missingReport,
allKnown,
unitSelections,
getUnit,
setUnit,
sciNotation,
toggleSciNotation,
addVariable,
addVariables,
removeVariable,
setValue,
addPreset,
applyPropellant,
clearWorkspace,
importWorkspace,
} = useSolver()
const [activeVarId, setActiveVarId] = useState(null)
const [showPropellants, setShowPropellants] = useState(false)
const [showEquations, setShowEquations] = useState(false)
const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 6 } })
)
function handleDragStart(event) {
const { varId } = event.active.data.current ?? {}
if (varId) setActiveVarId(varId)
}
function handleDragEnd(event) {
const { over, active } = event
if (over?.id === 'workspace') {
const { varId } = active.data.current ?? {}
if (varId) addVariable(varId)
}
setActiveVarId(null)
}
async function handleExportOdt() {
const data = buildExportData(workspaceVarIds, userValues, solved, unitSelections, getUnit, sciNotation)
const blob = await generateOdt(data)
downloadBlob(blob, 'rocketry-workspace.odt')
}
function handleExportJSON() {
const data = buildExportData(workspaceVarIds, userValues, solved, unitSelections, getUnit, sciNotation)
const blob = exportJSON(data, workspaceVarIds, userValues, unitSelections)
downloadBlob(blob, 'rocketry-workspace.json')
}
function handleImportJSON(jsonString) {
try {
const workspace = parseImport(jsonString)
importWorkspace(workspace)
} catch (err) {
alert(`Import failed: ${err.message}`)
}
}
return (
<DndContext sensors={sensors} onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
<div className="flex flex-1 min-h-0 overflow-hidden flex-col">
{/* Solver sub-header */}
<div className="flex items-center gap-2 px-5 py-2 border-b border-slate-700 bg-slate-900 shrink-0">
<p className="text-xs text-slate-400">
Add variables, enter known values, and unknowns solve automatically.
</p>
<div className="ml-auto flex items-center gap-2">
<button
onClick={toggleSciNotation}
title="Toggle scientific notation"
className={`flex items-center gap-1.5 px-3 py-2 rounded-lg border text-sm font-medium transition-colors ${
sciNotation
? 'bg-blue-700 border-blue-500 text-white'
: 'bg-slate-700 border-slate-600 text-slate-300 hover:bg-slate-600 hover:border-slate-500'
}`}
>
10ˣ
</button>
<button
onClick={() => setShowEquations(true)}
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 border border-slate-600 hover:border-slate-500 text-sm text-slate-200 font-medium transition-colors"
>
<span></span> Equations
</button>
<button
onClick={() => setShowPropellants(true)}
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-700 hover:bg-slate-600 border border-slate-600 hover:border-slate-500 text-sm text-slate-200 font-medium transition-colors"
>
<span></span> Propellants
</button>
</div>
</div>
{/* Three-panel layout */}
<div className="flex flex-1 min-h-0 overflow-hidden">
<VariablePalette
workspaceVarIds={workspaceVarIds}
onAddVariable={addVariable}
/>
<Workspace
workspaceVarIds={workspaceVarIds}
userValues={userValues}
solved={solved}
onRemove={removeVariable}
onValueChange={setValue}
onAddPreset={addPreset}
onClear={clearWorkspace}
getUnit={getUnit}
setUnit={setUnit}
sciNotation={sciNotation}
/>
<ResultsPanel
workspaceVarIds={workspaceVarIds}
userValues={userValues}
solved={solved}
missingReport={missingReport}
getUnit={getUnit}
sciNotation={sciNotation}
onExportOdt={handleExportOdt}
onExportJSON={handleExportJSON}
onImportJSON={handleImportJSON}
/>
</div>
</div>
{showPropellants && (
<PropellantModal
onClose={() => setShowPropellants(false)}
onApply={applyPropellant}
existingVarIds={workspaceVarIds}
/>
)}
{showEquations && (
<EquationBrowser
equations={EQUATIONS}
allKnown={allKnown}
workspaceVarIds={workspaceVarIds}
onAddVariables={varIds => addVariables(varIds)}
onClose={() => setShowEquations(false)}
/>
)}
<DragOverlay dropAnimation={null}>
{activeVarId ? (
<div className="opacity-90 rotate-2 scale-105">
<PaletteCard varId={activeVarId} isInWorkspace={false} />
</div>
) : null}
</DragOverlay>
</DndContext>
)
}