import React, { useMemo, useState, useEffect } from "react"; import { Upload, SplitSquareHorizontal, Maximize2, FileText, Download, Plus, Trash2, RotateCcw, Search, Columns3, PanelLeftClose, PanelRightClose } from "lucide-react"; function formatNumber(value) { const num = Number(value); if (!Number.isFinite(num)) return "—"; return num.toLocaleString(undefined, { maximumFractionDigits: 1 }); } function fileKind(file) { if (!file) return "none"; if (file.type?.startsWith("image/")) return "image"; if (file.type === "application/pdf" || file.name?.toLowerCase().endsWith(".pdf")) return "pdf"; return "other"; } function Field({ label, value, onChange, placeholder }) { return ( ); } function UploadBox({ side, file, setFile, meta, setMeta, zoom, setZoom, rotation, setRotation, panelMode }) { const [objectUrl, setObjectUrl] = useState(""); const kind = fileKind(file); useEffect(() => { if (!file) { setObjectUrl(""); return; } const url = URL.createObjectURL(file); setObjectUrl(url); return () => URL.revokeObjectURL(url); }, [file]); const iframeSrc = useMemo(() => { if (!objectUrl) return ""; if (kind === "pdf") return `${objectUrl}#toolbar=1&navpanes=0&zoom=${Math.round(zoom * 100)}`; return objectUrl; }, [objectUrl, kind, zoom]); return (

Chart {side === "left" ? "A" : "B"}

Upload PDF, PNG, JPG, or chart screenshot

setMeta({ ...meta, model: v })} placeholder="LTM 1130-5.1" /> setMeta({ ...meta, setup: v })} placeholder="360°, full counterweight" /> setMeta({ ...meta, boom: v })} placeholder="197' main / 35' jib" /> setMeta({ ...meta, notes: v })} placeholder="Outriggers, offset, deductions..." />
{file ? file.name : "No file uploaded"}
{Math.round(zoom * 100)}%
{!file && (

Drop in a load chart for side-by-side viewing

Use the table below to compare exact capacities by radius, boom length, counterweight, and chart percentage.

)} {file && kind === "pdf" && (