<template>
  <div class="spritesheet-preview w-full h-full">
    <div class="flex flex-col lg:flex-row gap-4 h-full">
      <div class="flex-1 min-w-0 flex flex-col">
        <div class="relative bg-gray-50 dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg overflow-auto flex-1 min-h-[300px] max-h-[calc(100vh-12rem)] shadow-sm hover:shadow-md transition-shadow duration-200">
          <canvas
            ref="previewCanvasRef"
            @mousedown="startDrag"
            @mousemove="drag"
            @mouseup="stopDrag"
            @mouseleave="stopDrag"
            @touchstart="handleTouchStart"
            @touchmove="handleTouchMove"
            @touchend="stopDrag"
            class="block touch-manipulation"
            :class="{ 'cursor-move': isDraggable }"
            :style="{ transform: `scale(${zoom})`, transformOrigin: 'top left', ...(settingsStore.pixelPerfect ? { 'image-rendering': 'pixelated' } : {}) }"
          >
          </canvas>

          <!-- Mobile zoom controls -->
          <div class="absolute bottom-3 right-3 flex space-x-2 lg:hidden bg-white/80 dark:bg-gray-800/80 p-2 rounded-lg shadow-md">
            <button @click="increaseZoom" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
              <i class="fas fa-plus"></i>
            </button>
            <button @click="decreaseZoom" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
              <i class="fas fa-minus"></i>
            </button>
          </div>
        </div>
      </div>

      <div class="lg:w-80 xl:w-96 flex-shrink-0">
        <div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4 space-y-4">
          <div class="space-y-3">
            <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Playback</h3>
            <div class="flex items-center gap-2">
              <button @click="togglePlayback" class="flex items-center justify-center gap-1.5 bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md transition-colors cursor-pointer flex-1">
                <span v-if="isPlaying" class="flex items-center gap-1.5">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5">
                    <path fill-rule="evenodd" d="M6.75 5.25a.75.75 0 01.75-.75H9a.75.75 0 01.75.75v13.5a.75.75 0 01-.75.75H7.5a.75.75 0 01-.75-.75V5.25zm7.5 0A.75.75 0 0115 4.5h1.5a.75.75 0 01.75.75v13.5a.75.75 0 01-.75.75H15a.75.75 0 01-.75-.75V5.25z" clip-rule="evenodd" />
                  </svg>
                  Pause
                </span>
                <span v-else class="flex items-center gap-1.5">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5">
                    <path fill-rule="evenodd" d="M4.5 5.653c0-1.426 1.529-2.33 2.779-1.643l11.54 6.348c1.295.712 1.295 2.573 0 3.285L7.28 19.991c-1.25.687-2.779-.217-2.779-1.643V5.653z" clip-rule="evenodd" />
                  </svg>
                  Play
                </span>
              </button>
              <button @click="previousFrame" class="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 p-2 rounded-md transition-colors duration-200 cursor-pointer" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5 dark:text-gray-200">
                  <path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 01.75.75v12.06l4.72-4.72a.75.75 0 111.06 1.06l-6 6a.75.75 0 01-1.06 0l-6-6a.75.75 0 011.06-1.06l4.72 4.72V5.97a.75.75 0 01.75-.75z" clip-rule="evenodd" transform="rotate(90 12 12)" />
                </svg>
              </button>
              <button @click="nextFrame" class="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 p-2 rounded-md transition-colors duration-200 cursor-pointer" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5 dark:text-gray-200">
                  <path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 01.75.75v12.06l4.72-4.72a.75.75 0 111.06 1.06l-6 6a.75.75 0 01-1.06 0l-6-6a.75.75 0 011.06-1.06l4.72 4.72V5.97a.75.75 0 01.75-.75z" clip-rule="evenodd" transform="rotate(-90 12 12)" />
                </svg>
              </button>
            </div>
          </div>

          <!-- Sliders -->
          <div class="space-y-3">
            <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Controls</h3>

            <!-- Frame Navigation -->
            <div class="space-y-1">
              <div class="flex justify-between items-center">
                <span class="text-xs font-medium text-gray-600 dark:text-gray-400">Frame</span>
                <span class="text-xs font-mono text-gray-700 dark:text-gray-300">{{ visibleFrameNumber }}/{{ visibleFramesCount }}</span>
              </div>
              <input type="range" min="0" :max="visibleFrames.length - 1" :value="visibleFrameIndex" @input="handleSliderInput" class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" :disabled="isPlaying" :class="{ 'opacity-50': isPlaying }" />
            </div>

            <!-- FPS Control -->
            <div class="space-y-1">
              <div class="flex justify-between items-center">
                <span class="text-xs font-medium text-gray-600 dark:text-gray-400">FPS</span>
                <span class="text-xs font-mono text-gray-700 dark:text-gray-300">{{ fps }}</span>
              </div>
              <input type="range" min="1" max="60" v-model.number="fps" class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" />
            </div>

            <!-- Zoom Control -->
            <div class="space-y-1">
              <div class="flex justify-between items-center">
                <span class="text-xs font-medium text-gray-600 dark:text-gray-400">Zoom</span>
                <span class="text-xs font-mono text-gray-700 dark:text-gray-300">{{ Math.round(zoom * 100) }}%</span>
              </div>
              <input type="range" min="0.5" max="5" step="0.1" v-model.number="zoom" class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" />
            </div>
          </div>

          <!-- Options -->
          <div class="space-y-3">
            <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Options</h3>
            <div class="space-y-2">
              <label class="flex items-center gap-2 cursor-pointer">
                <input type="checkbox" v-model="isDraggable" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" />
                <span class="text-sm dark:text-gray-200">Reposition</span>
              </label>

              <label class="flex items-center gap-2 cursor-pointer ml-4" :class="{ 'opacity-50 cursor-not-allowed': !isDraggable }">
                <input type="checkbox" v-model="repositionAllLayers" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" :disabled="!isDraggable" />
                <span class="text-sm dark:text-gray-200">All layers</span>
              </label>

              <label class="flex items-center gap-2 cursor-pointer">
                <input type="checkbox" v-model="showAllSprites" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" />
                <span class="text-sm dark:text-gray-200">Compare sprites</span>
              </label>

              <label class="flex items-center gap-2 cursor-pointer">
                <input type="checkbox" v-model="settingsStore.pixelPerfect" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" @change="drawPreviewCanvas" />
                <span class="text-sm dark:text-gray-200">Pixel perfect</span>
              </label>
            </div>
          </div>

          <!-- Current frame offset display -->
          <div v-if="currentFrameSprite" class="p-2 bg-gray-100 dark:bg-gray-700 rounded-md border border-gray-200 dark:border-gray-600">
            <div class="flex items-center justify-between">
              <span class="text-xs font-medium text-gray-600 dark:text-gray-400">Offset</span>
              <span class="text-xs font-mono font-semibold text-cyan-600 dark:text-cyan-400">x: {{ currentFrameSprite.x }}, y: {{ currentFrameSprite.y }}</span>
            </div>
          </div>

          <!-- Frame Selection (when Compare sprites is enabled) -->
          <div v-if="showAllSprites" class="space-y-2">
            <div class="flex items-center justify-between">
              <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Frames</h3>
              <div class="flex gap-1">
                <button @click="showAllFrames" class="px-2 py-1 text-xs bg-blue-500 hover:bg-blue-600 text-white rounded transition-colors">All</button>
                <button @click="hideAllFrames" class="px-2 py-1 text-xs bg-gray-500 hover:bg-gray-600 text-white rounded transition-colors">None</button>
              </div>
            </div>
            <div class="rounded-md border border-gray-300 dark:border-gray-600 dark:bg-gray-800">
              <div class="max-h-[180px] overflow-y-auto">
                <div class="space-y-0.5 p-1">
                  <div v-for="(sprite, index) in compositeFrames" :key="sprite.id" class="flex items-center gap-2 px-2 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer rounded" @click="toggleHiddenFrame(index)">
                    <input type="checkbox" :checked="!hiddenFrames.includes(index)" class="w-3.5 h-3.5 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" @click.stop @change="toggleHiddenFrame(index)" />
                    <div class="w-8 h-8 bg-gray-100 dark:bg-gray-700 rounded flex items-center justify-center overflow-hidden flex-shrink-0">
                      <img :src="sprite.img.src" class="max-w-full max-h-full object-contain" :style="settingsStore.pixelPerfect ? { 'image-rendering': 'pixelated' } : {}" />
                    </div>
                    <span class="text-xs dark:text-gray-200">{{ index + 1 }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, onMounted, watch, onUnmounted, computed } from 'vue';
  import { useSettingsStore } from '@/stores/useSettingsStore';
  import type { Layer, Sprite } from '@/types/sprites';
  import { getMaxDimensionsAcrossLayers } from '@/composables/useLayers';
  import { useCanvas2D } from '@/composables/useCanvas2D';
  import { useZoom } from '@/composables/useZoom';
  import { useAnimationFrames } from '@/composables/useAnimationFrames';
  import { calculateNegativeSpacing } from '@/composables/useNegativeSpacing';

  const props = defineProps<{
    layers: Layer[];
    activeLayerId: string;
    columns: number;
  }>();

  const emit = defineEmits<{
    (e: 'updateSprite', id: string, x: number, y: number): void;
    (e: 'updateSpriteInLayer', layerId: string, spriteId: string, x: number, y: number): void;
  }>();

  const previewCanvasRef = ref<HTMLCanvasElement | null>(null);

  // Get settings from store
  const settingsStore = useSettingsStore();

  // Initialize composables
  const canvas2D = useCanvas2D(previewCanvasRef);

  const {
    zoom,
    increase: increaseZoom,
    decrease: decreaseZoom,
  } = useZoom({
    allowedValues: [0.5, 1, 2, 3, 4, 5],
    initial: 1,
  });

  const getVisibleLayers = () => props.layers.filter(l => l.visible);
  const maxFrames = () => Math.max(0, ...getVisibleLayers().map(l => l.sprites.length));

  const { currentFrameIndex, isPlaying, fps, hiddenFrames, visibleFrames, visibleFramesCount, visibleFrameIndex, visibleFrameNumber, togglePlayback, nextFrame, previousFrame, handleSliderInput, toggleHiddenFrame, showAllFrames, hideAllFrames, stopAnimation } = useAnimationFrames({
    sprites: () => {
      const len = maxFrames();
      const frames: Sprite[] = [];
      for (let i = 0; i < len; i++) {
        const s = getVisibleLayers().find(l => l.sprites[i])?.sprites[i];
        if (s) frames.push(s);
      }
      return frames;
    },
    onDraw: drawPreviewCanvas,
  });

  // Preview state
  const isDraggable = ref(false);
  const repositionAllLayers = ref(false);
  const showAllSprites = ref(false);

  const compositeFrames = computed<Sprite[]>(() => {
    // Show frames from the active layer for the thumbnail list
    const activeLayer = props.layers.find(l => l.id === props.activeLayerId);
    if (!activeLayer) {
      // Fallback to first visible layer if no active layer
      const v = getVisibleLayers();
      const len = maxFrames();
      const arr: Sprite[] = [];
      for (let i = 0; i < len; i++) {
        const s = v.find(l => l.sprites[i])?.sprites[i];
        if (s) arr.push(s);
      }
      return arr;
    }
    return activeLayer.sprites;
  });

  const currentFrameSprite = computed<Sprite | null>(() => {
    const layer = props.layers.find(l => l.id === props.activeLayerId);
    if (!layer) return null;
    return layer.sprites[currentFrameIndex.value] || null;
  });

  // Dragging state
  const isDragging = ref(false);
  const activeSpriteId = ref<string | null>(null);
  const dragStartX = ref(0);
  const dragStartY = ref(0);
  const spritePosBeforeDrag = ref({ x: 0, y: 0 });
  const allSpritesPosBeforeDrag = ref<Map<string, { x: number; y: number }>>(new Map());

  // Canvas drawing

  const getCellDimensions = () => {
    const visibleLayers = getVisibleLayers();
    // If manual cell size is enabled, use manual values
    if (settingsStore.manualCellSizeEnabled) {
      return {
        cellWidth: settingsStore.manualCellWidth,
        cellHeight: settingsStore.manualCellHeight,
        negativeSpacing: 0,
      };
    }

    // Otherwise, calculate from sprite dimensions
    const { maxWidth, maxHeight } = getMaxDimensionsAcrossLayers(visibleLayers);
    const allSprites = visibleLayers.flatMap(l => l.sprites);
    const negativeSpacing = calculateNegativeSpacing(allSprites, settingsStore.negativeSpacingEnabled);
    return {
      cellWidth: maxWidth + negativeSpacing,
      cellHeight: maxHeight + negativeSpacing,
      negativeSpacing,
    };
  };

  function drawPreviewCanvas() {
    if (!previewCanvasRef.value || !canvas2D.ctx.value) return;
    const visibleLayers = getVisibleLayers();
    if (!visibleLayers.length || !visibleLayers.some(l => l.sprites.length)) return;

    const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();

    // Apply pixel art optimization
    canvas2D.applySmoothing();

    // Set canvas size to fit one sprite cell (expanded with negative spacing)
    canvas2D.setCanvasSize(cellWidth, cellHeight);

    // Clear canvas
    canvas2D.clear();

    // Draw grid background (cell)
    canvas2D.fillRect(0, 0, cellWidth, cellHeight, '#f9fafb');

    const frameIndex = currentFrameIndex.value;

    if (showAllSprites.value) {
      // When comparing sprites, show all frames from all visible layers (dimmed)
      const len = maxFrames();
      for (let i = 0; i < len; i++) {
        if (i === frameIndex || hiddenFrames.value.includes(i)) continue;
        visibleLayers.forEach(layer => {
          const sprite = layer.sprites[i];
          if (!sprite) return;
          canvas2D.drawImage(sprite.img, negativeSpacing + sprite.x, negativeSpacing + sprite.y, 0.3);
        });
      }
    }

    // Always draw current frame from all visible layers
    visibleLayers.forEach(layer => {
      const sprite = layer.sprites[frameIndex];
      if (!sprite) return;
      canvas2D.drawImage(sprite.img, negativeSpacing + sprite.x, negativeSpacing + sprite.y);
    });

    // Draw cell border
    canvas2D.strokeRect(0, 0, cellWidth, cellHeight, '#e5e7eb', 1);
  }

  // Drag functionality
  const startDrag = (event: MouseEvent) => {
    if (!isDraggable.value || !previewCanvasRef.value) return;

    const rect = previewCanvasRef.value.getBoundingClientRect();
    const scaleX = previewCanvasRef.value.width / (rect.width / zoom.value);
    const scaleY = previewCanvasRef.value.height / (rect.height / zoom.value);

    const mouseX = ((event.clientX - rect.left) / zoom.value) * scaleX;
    const mouseY = ((event.clientY - rect.top) / zoom.value) * scaleY;

    const { negativeSpacing } = getCellDimensions();

    if (repositionAllLayers.value) {
      // Check if click is on any sprite from any visible layer
      const visibleLayers = getVisibleLayers();
      for (const layer of visibleLayers) {
        const sprite = layer.sprites[currentFrameIndex.value];
        if (!sprite) continue;

        const spriteCanvasX = negativeSpacing + sprite.x;
        const spriteCanvasY = negativeSpacing + sprite.y;
        if (mouseX >= spriteCanvasX && mouseX <= spriteCanvasX + sprite.width && mouseY >= spriteCanvasY && mouseY <= spriteCanvasY + sprite.height) {
          isDragging.value = true;
          activeSpriteId.value = 'ALL_LAYERS'; // Special marker for all layers
          dragStartX.value = mouseX;
          dragStartY.value = mouseY;

          // Store initial positions for all sprites in this frame from all visible layers
          allSpritesPosBeforeDrag.value.clear();
          visibleLayers.forEach(layer => {
            const s = layer.sprites[currentFrameIndex.value];
            if (s) {
              allSpritesPosBeforeDrag.value.set(s.id, { x: s.x, y: s.y });
            }
          });
          return;
        }
      }
    } else {
      // Only check active layer sprite
      const activeSprite = props.layers.find(l => l.id === props.activeLayerId)?.sprites[currentFrameIndex.value];
      if (activeSprite) {
        const spriteCanvasX = negativeSpacing + activeSprite.x;
        const spriteCanvasY = negativeSpacing + activeSprite.y;
        if (mouseX >= spriteCanvasX && mouseX <= spriteCanvasX + activeSprite.width && mouseY >= spriteCanvasY && mouseY <= spriteCanvasY + activeSprite.height) {
          isDragging.value = true;
          activeSpriteId.value = activeSprite.id;
          dragStartX.value = mouseX;
          dragStartY.value = mouseY;
          spritePosBeforeDrag.value = { x: activeSprite.x, y: activeSprite.y };
        }
      }
    }
  };

  const drag = (event: MouseEvent) => {
    if (!isDragging.value || !activeSpriteId.value || !previewCanvasRef.value) return;

    const rect = previewCanvasRef.value.getBoundingClientRect();
    const scaleX = previewCanvasRef.value.width / (rect.width / zoom.value);
    const scaleY = previewCanvasRef.value.height / (rect.height / zoom.value);

    const mouseX = ((event.clientX - rect.left) / zoom.value) * scaleX;
    const mouseY = ((event.clientY - rect.top) / zoom.value) * scaleY;

    const deltaX = Math.round(mouseX - dragStartX.value);
    const deltaY = Math.round(mouseY - dragStartY.value);

    const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();

    if (activeSpriteId.value === 'ALL_LAYERS') {
      // Move all sprites in current frame from all visible layers
      const visibleLayers = getVisibleLayers();
      visibleLayers.forEach(layer => {
        const sprite = layer.sprites[currentFrameIndex.value];
        if (!sprite) return;

        const originalPos = allSpritesPosBeforeDrag.value.get(sprite.id);
        if (!originalPos) return;

        // Calculate new position with constraints
        let newX = Math.round(originalPos.x + deltaX);
        let newY = Math.round(originalPos.y + deltaY);

        // Constrain movement within expanded cell
        newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - sprite.width, newX));
        newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - sprite.height, newY));

        emit('updateSpriteInLayer', layer.id, sprite.id, newX, newY);
      });
      drawPreviewCanvas();
    } else {
      // Move only the active layer sprite
      const activeSprite = props.layers.find(l => l.id === props.activeLayerId)?.sprites[currentFrameIndex.value];
      if (!activeSprite || activeSprite.id !== activeSpriteId.value) return;

      // Calculate new position with constraints and round to integers
      let newX = Math.round(spritePosBeforeDrag.value.x + deltaX);
      let newY = Math.round(spritePosBeforeDrag.value.y + deltaY);

      // Constrain movement within expanded cell (allow negative values up to -negativeSpacing)
      newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - activeSprite.width, newX));
      newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - activeSprite.height, newY));

      emit('updateSprite', activeSpriteId.value, newX, newY);
      drawPreviewCanvas();
    }
  };

  const stopDrag = () => {
    isDragging.value = false;
    activeSpriteId.value = null;
  };

  const handleTouchStart = (event: TouchEvent) => {
    if (!isDraggable.value) return;

    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = new MouseEvent('mousedown', {
        clientX: touch.clientX,
        clientY: touch.clientY,
      });
      startDrag(mouseEvent);
    }
  };

  const handleTouchMove = (event: TouchEvent) => {
    if (!isDraggable.value) return;

    if (isDragging.value) {
      event.preventDefault(); // Only prevent default when actually dragging
    }

    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = new MouseEvent('mousemove', {
        clientX: touch.clientX,
        clientY: touch.clientY,
      });
      drag(mouseEvent);
    }
  };

  // Lifecycle hooks
  onMounted(() => {
    canvas2D.initContext();
    drawPreviewCanvas();

    // Listen for forceRedraw event from App.vue
    window.addEventListener('forceRedraw', handleForceRedraw);
  });

  onUnmounted(() => {
    stopAnimation();
    window.removeEventListener('forceRedraw', handleForceRedraw);
  });

  // Handler for force redraw event
  const handleForceRedraw = () => {
    const allSprites = props.layers.flatMap(l => l.sprites);
    canvas2D.ensureIntegerPositions(allSprites);
    canvas2D.applySmoothing();
    drawPreviewCanvas();
  };

  // Watchers
  watch(() => props.layers, drawPreviewCanvas, { deep: true });
  watch(() => props.activeLayerId, drawPreviewCanvas);
  watch(currentFrameIndex, drawPreviewCanvas);
  watch(zoom, drawPreviewCanvas);
  watch(isDraggable, drawPreviewCanvas);
  watch(showAllSprites, drawPreviewCanvas);
  watch(hiddenFrames, drawPreviewCanvas);
  watch(() => settingsStore.pixelPerfect, drawPreviewCanvas);
  watch(() => settingsStore.negativeSpacingEnabled, drawPreviewCanvas);
  watch(() => settingsStore.manualCellSizeEnabled, drawPreviewCanvas);
  watch(() => settingsStore.manualCellWidth, drawPreviewCanvas);
  watch(() => settingsStore.manualCellHeight, drawPreviewCanvas);

  // Initial draw
  if (props.layers.some(l => l.sprites.length > 0)) {
    drawPreviewCanvas();
  }
</script>

<style scoped>
  /* Custom styling for range inputs */
  input[type='range']::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #3b82f6;
    cursor: pointer;
  }

  input[type='range']::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #3b82f6;
    cursor: pointer;
    border: none;
  }
</style>
