import { useState, useEffect } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2, Check, AlertTriangle, Printer, Eye, EyeOff, Info, ChevronDown, ExternalLink } from 'lucide-react';
import { virtualPrinterApi } from '../api/client';
import { Card, CardContent, CardHeader } from './Card';
import { Button } from './Button';
import { useToast } from '../contexts/ToastContext';
export function VirtualPrinterSettings() {
const queryClient = useQueryClient();
const { showToast } = useToast();
const [localEnabled, setLocalEnabled] = useState(false);
const [localAccessCode, setLocalAccessCode] = useState('');
const [localMode, setLocalMode] = useState<'immediate' | 'queue'>('immediate');
const [localModel, setLocalModel] = useState('3DPrinter-X1-Carbon');
const [showAccessCode, setShowAccessCode] = useState(false);
const [pendingAction, setPendingAction] = useState<'toggle' | 'accessCode' | 'mode' | 'model' | null>(null);
// Fetch current settings
const { data: settings, isLoading } = useQuery({
queryKey: ['virtual-printer-settings'],
queryFn: virtualPrinterApi.getSettings,
refetchInterval: 10000, // Refresh every 10 seconds for status updates
});
// Fetch available models
const { data: modelsData } = useQuery({
queryKey: ['virtual-printer-models'],
queryFn: virtualPrinterApi.getModels,
});
// Initialize local state from settings
useEffect(() => {
if (settings) {
setLocalEnabled(settings.enabled);
setLocalMode(settings.mode);
setLocalModel(settings.model);
}
}, [settings]);
// Update mutation
const updateMutation = useMutation({
mutationFn: (data: { enabled?: boolean; access_code?: string; mode?: 'immediate' | 'queue'; model?: string }) =>
virtualPrinterApi.updateSettings(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['virtual-printer-settings'] });
showToast('Virtual printer settings updated');
setPendingAction(null);
},
onError: (error: Error) => {
showToast(error.message || 'Failed to update settings', 'error');
// Revert local state on error
if (settings) {
setLocalEnabled(settings.enabled);
setLocalMode(settings.mode);
setLocalModel(settings.model);
}
setPendingAction(null);
},
});
const handleToggleEnabled = () => {
const newEnabled = !localEnabled;
// If enabling, must have access code
if (newEnabled && !localAccessCode && !settings?.access_code_set) {
showToast('Please set an access code first', 'error');
return;
}
setLocalEnabled(newEnabled);
setPendingAction('toggle');
updateMutation.mutate({
enabled: newEnabled,
access_code: localAccessCode || undefined,
mode: localMode,
});
};
const handleAccessCodeChange = () => {
if (!localAccessCode) {
showToast('Access code cannot be empty', 'error');
return;
}
if (localAccessCode.length !== 8) {
showToast('Access code must be exactly 8 characters', 'error');
return;
}
setPendingAction('accessCode');
updateMutation.mutate({
access_code: localAccessCode,
});
setLocalAccessCode(''); // Clear after saving
};
const handleModeChange = (mode: 'immediate' | 'queue') => {
setLocalMode(mode);
setPendingAction('mode');
updateMutation.mutate({ mode });
};
const handleModelChange = (model: string) => {
setLocalModel(model);
setPendingAction('model');
updateMutation.mutate({ model });
};
if (isLoading) {
return (
Enable a virtual printer that appears in Bambu Studio and OrcaSlicer. Files sent to this printer will be archived directly without printing.
{/* Enable/Disable Toggle */}
Must be exactly 8 characters. Used by slicers to authenticate. {localAccessCode && ( {' '}({localAccessCode.length}/8) )}
Setup Required
The virtual printer feature requires additional system configuration before it will work. This includes port forwarding, firewall rules, and platform-specific settings.
How it works: