"use client"; // T06 — drag-to-reorder editor for the stacked-compression pipeline (gaps v3.8.42). // // A controlled component: it renders `steps` and reports every edit back through `onChange` // (parent owns the state + persistence). All mutations go through the pure // `compressionPipelineModel` so invariants (valid intensity, non-empty pipeline) hold. // import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, type DragEndEvent, } from "@dnd-kit/core"; import { SortableContext, useSortable, verticalListSortingStrategy, sortableKeyboardCoordinates, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { useTranslations } from "next-intl"; import { allowedIntensities, addLayer, moveLayer, removeLayer, updateLayer, type EngineIntensities, type PipelineStep, } from "./compressionPipelineModel"; export type { PipelineStep } from "./compressionPipelineModel"; type Props = { steps: PipelineStep[]; onChange: (steps: PipelineStep[]) => void; engineIntensities: EngineIntensities; }; const SELECT_CLASS = "rounded-lg border border-border bg-bg px-3 py-2 text-sm text-text-main w-full"; function SortableRow(props: { id: string; index: number; step: PipelineStep; engines: string[]; engineIntensities: EngineIntensities; canRemove: boolean; onPatch: (patch: Partial) => void; onRemove: () => void; }) { const t = useTranslations("contextCombos"); const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: props.id, }); const style: React.CSSProperties = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.6 : 1, }; return (
); } export function CompressionPipelineEditor({ steps, onChange, engineIntensities }: Props) { const t = useTranslations("contextCombos"); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); // Index-based stable ids: a controlled list with no per-row id. Ids are stable within a // render (0..n-1); reorder maps id→index before delegating to the pure model. const ids = steps.map((_, index) => String(index)); const engines = Object.keys(engineIntensities); const firstEngine = engines[0] ?? "rtk"; const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) return; const from = ids.indexOf(String(active.id)); const to = ids.indexOf(String(over.id)); onChange(moveLayer(steps, from, to)); }; return (

{t("pipeline")}

{steps.map((step, index) => ( 1} onPatch={(patch) => onChange(updateLayer(steps, index, patch, engineIntensities))} onRemove={() => onChange(removeLayer(steps, index))} /> ))}
); }