import { Maximize2 } from 'lucide-react'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useI18n } from '../../application/i18n/I18nProvider'; import { STORAGE_KEY_SNIPPET_SCRIPT_EDITOR_HEIGHT } from '@/infrastructure/config/storageKeys.ts'; import { localStorageAdapter } from '@/infrastructure/persistence/localStorageAdapter.ts'; import { Button } from '../ui/button'; import { ScriptCodeEditor, type ScriptCodeEditorHandle, } from '../scripts/ScriptCodeEditor'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from '../ui/dialog'; import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'; const DEFAULT_HEIGHT = 120; const MIN_HEIGHT = 80; const MAX_HEIGHT = 520; function clampHeight(height: number, minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT): number { return Math.max(minHeight, Math.min(maxHeight, height)); } function readStoredHeight({ defaultHeight, minHeight, maxHeight, persistHeight, }: { defaultHeight: number; minHeight: number; maxHeight: number; persistHeight: boolean; }): number { if (!persistHeight) return clampHeight(defaultHeight, minHeight, maxHeight); const stored = localStorageAdapter.readNumber(STORAGE_KEY_SNIPPET_SCRIPT_EDITOR_HEIGHT); if (stored === null) return clampHeight(defaultHeight, minHeight, maxHeight); return clampHeight(stored, minHeight, maxHeight); } export interface SnippetScriptEditorProps { value: string; onChange: (value: string) => void; placeholder?: string; id?: string; /** Shown on the same row as the expand button (e.g. "Script *"). */ label?: string; defaultHeight?: number; minHeight?: number; maxHeight?: number; persistHeight?: boolean; /** Save or submit the surrounding form with Cmd/Ctrl+Enter. */ onSubmitShortcut?: () => void; } export const SnippetScriptEditor: React.FC = ({ value, onChange, placeholder, id, label, defaultHeight = DEFAULT_HEIGHT, minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT, persistHeight = true, onSubmitShortcut, }) => { const { t } = useI18n(); const [height, setHeight] = useState(() => readStoredHeight({ defaultHeight, minHeight, maxHeight, persistHeight, })); const [modalOpen, setModalOpen] = useState(false); const dragRef = useRef<{ startY: number; startHeight: number } | null>(null); const inlineEditorRef = useRef(null); const heightRef = useRef(height); heightRef.current = height; const handleResizeStart = useCallback((e: React.MouseEvent) => { e.preventDefault(); dragRef.current = { startY: e.clientY, startHeight: heightRef.current }; document.body.style.cursor = 'ns-resize'; document.body.style.userSelect = 'none'; }, []); useEffect(() => { const onMove = (e: MouseEvent) => { if (!dragRef.current) return; const delta = e.clientY - dragRef.current.startY; setHeight(clampHeight(dragRef.current.startHeight + delta, minHeight, maxHeight)); }; const onUp = () => { if (dragRef.current && persistHeight) { localStorageAdapter.writeNumber( STORAGE_KEY_SNIPPET_SCRIPT_EDITOR_HEIGHT, heightRef.current, ); } dragRef.current = null; document.body.style.cursor = ''; document.body.style.userSelect = ''; }; window.addEventListener('mousemove', onMove); window.addEventListener('mouseup', onUp); return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); document.body.style.cursor = ''; document.body.style.userSelect = ''; }; }, [maxHeight, minHeight, persistHeight]); return ( <>
{label ? ( id ? ( ) : (

{label}

) ) : ( )} {t('snippets.scriptEditor.expand')}
{t('snippets.scriptEditor.modalTitle')}
); };