import { Loader2, Play, X } from 'lucide-react'; import React, { useCallback, useMemo, useState } from 'react'; import { useI18n } from '@/application/i18n/I18nProvider'; import type { Host, Snippet } from '@/domain/models'; import { DEFAULT_SCRIPT_TEMPLATE } from '@/domain/snippetScript.ts'; import { scheduleWindowInputFocus } from '@/application/state/windowInputFocus'; import { SelectHostDialog } from '@/components/SelectHostDialog'; import { SelectGroupDialog } from '@/components/SelectGroupDialog'; import { ScriptCodeEditor } from './ScriptCodeEditor'; import { ScriptMetaFields } from './ScriptMetaFields'; import { SnippetTargetsSection } from '@/components/snippets/SnippetTargetsSection'; import { resolveSnippetTargetGroupsForSave } from '@/domain/snippetTargets.ts'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; export interface ScriptEditorModalProps { open: boolean; onClose: () => void; snippet: Snippet; onChange: (snippet: Snippet) => void; onSave?: () => void; onRun?: () => void; canRun?: boolean; targetHosts: Host[]; hosts: Host[]; customGroups?: string[]; selectedHostIds: string[]; onSelectHost: (host: Host) => void; onSelectionChange?: (selectedHostIds: string[]) => void; selectedGroupPaths?: string[]; onGroupSelectionChange?: (selectedGroupPaths: string[]) => void; targetsAllHosts?: boolean; onTargetsAllHostsChange?: (checked: boolean) => void; } function countLines(content: string): number { if (!content) return 1; let lines = 1; for (let i = 0; i < content.length; i += 1) { if (content.charCodeAt(i) === 10) lines += 1; } return lines; } export const ScriptEditorModal: React.FC = ({ open, onClose, snippet, onChange, onSave, onRun, canRun = false, targetHosts, hosts, customGroups = [], selectedHostIds, onSelectHost, onSelectionChange, selectedGroupPaths = [], onGroupSelectionChange, targetsAllHosts = false, onTargetsAllHostsChange, }) => { const { t } = useI18n(); const [targetPickerOpen, setTargetPickerOpen] = useState(false); const [groupPickerOpen, setGroupPickerOpen] = useState(false); const handleOpenChange = useCallback((isOpen: boolean) => { if (!isOpen) { onClose(); scheduleWindowInputFocus(); } }, [onClose]); const handleClose = useCallback(() => { onClose(); scheduleWindowInputFocus(); }, [onClose]); const handleSave = useCallback(() => { onSave?.(); onClose(); scheduleWindowInputFocus(); }, [onClose, onSave]); const handleTargetsConfirm = useCallback(() => { onChange({ ...snippet, targets: selectedHostIds, targetGroups: resolveSnippetTargetGroupsForSave(snippet, selectedGroupPaths), targetsAllHosts: undefined, }); }, [onChange, selectedGroupPaths, selectedHostIds, snippet]); const language = 'javascript'; const editorValue = snippet.command || DEFAULT_SCRIPT_TEMPLATE; const title = snippet.label?.trim() || t('scripts.editor.modalTitle'); const lineCount = useMemo(() => countLines(editorValue), [editorValue]); return ( <> {title}
{title}
{onRun ? ( {t('scripts.actions.runNowHint')} ) : null}
{ if (!targetsAllHosts) setTargetPickerOpen(true); }} onEditGroups={onGroupSelectionChange ? () => { if (!targetsAllHosts) setGroupPickerOpen(true); } : undefined} hint={t('scripts.targets.hint')} targetsAllHosts={targetsAllHosts} onTargetsAllHostsChange={onTargetsAllHostsChange} />
{open ? ( onChange({ ...snippet, command })} language={language} fill minimap active={open} /> ) : (
)}
JavaScript {t('scripts.editor.lineCount', { count: lineCount })}
{ onGroupSelectionChange?.(nextGroups); onChange({ ...snippet, targetGroups: nextGroups, targetsAllHosts: undefined, }); }} /> ); };