import { useState } from 'react'; import { Settings, ChevronDown, ChevronUp, Flame } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import type { PrintOptionsProps, PrintOptions as PrintOptionsType, PreheatOverride, CalibrationMode, } from './types'; import { CALIBRATION_MODES, CALIBRATION_MODE_ACTIVE, CALIBRATION_MODE_INACTIVE, } from '../../utils/calibrationMode'; import { MAX_CHAMBER_TEMP_C } from '../../utils/printer'; type OptionConfig = { key: keyof PrintOptionsType; label: string; desc: string; dualNozzleOnly?: boolean; /** Tri-state (off/on/auto) rather than a plain on/off pair. */ tristate?: boolean; }; // On/off options render as the same button pair, minus the "auto" choice. const BOOLEAN_MODES = ['off', 'on'] as const; /** * Print options toggle panel with collapsible UI. * Shows bed levelling, flow/vibration calibration, layer inspection, timelapse, * and (for dual-nozzle printers only) nozzle offset calibration. */ export function PrintOptionsPanel({ options, onChange, defaultExpanded = false, showDualNozzleOptions = false, }: PrintOptionsProps) { const { t } = useTranslation(); const [isExpanded, setIsExpanded] = useState(defaultExpanded); // Labels/descriptions reuse the settings.default* namespace — identical strings, // already translated across all locales. Only nozzle_offset_cali is new (#1682). const printOptionsConfig: OptionConfig[] = [ { key: 'bed_levelling', label: t('settings.defaultBedLevelling'), desc: t('settings.defaultBedLevellingDesc'), tristate: true }, { key: 'flow_cali', label: t('settings.defaultFlowCali'), desc: t('settings.defaultFlowCaliDesc'), tristate: true }, { key: 'vibration_cali', label: t('settings.defaultVibrationCali'), desc: t('settings.defaultVibrationCaliDesc') }, { key: 'layer_inspect', label: t('settings.defaultLayerInspect'), desc: t('settings.defaultLayerInspectDesc') }, { key: 'timelapse', label: t('settings.defaultTimelapse'), desc: t('settings.defaultTimelapseDesc') }, { key: 'nozzle_offset_cali', label: t('settings.defaultNozzleOffsetCali'), desc: t('settings.defaultNozzleOffsetCaliDesc'), dualNozzleOnly: true, tristate: true }, ]; const visibleOptions = printOptionsConfig.filter(o => !o.dualNozzleOnly || showDualNozzleOptions); const handleToggle = (key: keyof PrintOptionsType, value: boolean) => { onChange({ ...options, [key]: value }); }; const handleCalibrationMode = (key: keyof PrintOptionsType, mode: CalibrationMode) => { onChange({ ...options, [key]: mode }); }; const handlePreheatOverride = (next: PreheatOverride) => { onChange({ ...options, preheat_override: next, // Clearing override→off also clears the chamber-target override so the // backend doesn't carry a stale value if the user re-enables later. ...(next === 'off' ? { preheat_chamber_target_override: null } : {}), }); }; const handlePreheatTarget = (raw: string) => { if (raw === '') { onChange({ ...options, preheat_chamber_target_override: null }); return; } const parsed = parseInt(raw, 10); if (Number.isNaN(parsed)) return; onChange({ ...options, preheat_chamber_target_override: Math.max(0, Math.min(MAX_CHAMBER_TEMP_C, parsed)), }); }; return (
{isExpanded && (
{visibleOptions.map(({ key, label, desc, tristate }) => tristate ? (
{label}

{desc}

{CALIBRATION_MODES.map((mode) => ( ))}
) : (
{label}

{desc}

{BOOLEAN_MODES.map((mode) => { const active = (options[key as 'vibration_cali'] ? 'on' : 'off') === mode; return ( ); })}
), )} {/* Preheat / heat-soak per-item override (#1468). Defaults to 'inherit' which means the global Settings → Workflow toggle decides. Forcing 'on' or 'off' overrides per-print; the chamber target override (optional °C input, visible when not 'off') bypasses the per-filament-type derivation. */}
{t('settings.preheatTitle', 'Preheat & Heat Soak')}

{t('settings.preheatPerItemDesc', 'Heat the bed and chamber before this print starts. Defaults to the global Settings → Workflow toggle.')}

{(['inherit', 'on', 'off'] as PreheatOverride[]).map((opt) => ( ))}
{options.preheat_override !== 'off' && (
handlePreheatTarget(e.target.value)} placeholder="—" className="w-16 px-2 py-1 bg-bambu-dark-tertiary border border-bambu-dark-tertiary rounded text-white text-xs text-right focus:outline-none focus:border-bambu-green" />
)}
)}
); }