import { useState } from 'react'; import { Loader2 } from 'lucide-react'; import { Modal, ModalHeader, ModalBody, ModalFooter } from '@/components/ui/modal'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { toast } from '@/components/ui/toast-store'; import { writeStackFile } from '@/lib/stackFilesApi'; function isValidFileName(name: string): boolean { if (!name || name === '.' || name === '..') return false; return /^[^/\\]+$/.test(name); } interface NewFileDialogProps { open: boolean; onOpenChange: (open: boolean) => void; stackName: string; /** Directory within the stack where the file will be created */ currentDir: string; onCreated: () => void; } export function NewFileDialog({ open, onOpenChange, stackName, currentDir, onCreated, }: NewFileDialogProps) { const [name, setName] = useState(''); const [creating, setCreating] = useState(false); const [validationError, setValidationError] = useState(null); const handleClose = (next: boolean) => { if (creating) return; onOpenChange(next); if (!next) { setName(''); setValidationError(null); } }; const handleCreate = async () => { const trimmed = name.trim(); if (!isValidFileName(trimmed)) { setValidationError('File name must not be empty and must not contain / or \\.'); return; } setValidationError(null); setCreating(true); const relPath = currentDir ? `${currentDir}/${trimmed}` : trimmed; try { await writeStackFile(stackName, relPath, ''); toast.success('File created.'); onCreated(); onOpenChange(false); setName(''); } catch (e) { toast.error(e instanceof Error ? e.message : 'Failed to create file.'); } finally { setCreating(false); } }; const parentLabel = currentDir || stackName; return (
{ setName(e.target.value); setValidationError(null); }} onKeyDown={(e) => { if (e.key === 'Enter') void handleCreate(); }} placeholder="config.yaml" disabled={creating} autoFocus /> {validationError && (

{validationError}

)}
handleClose(false)} disabled={creating}> Cancel } primary={ } />
); }