/* ============================================================
   2D 像素編輯器視圖 — Pixel Editor
   完整功能：繪圖、圖層、縮放平移、網格、對稱、雜訊、陰影
   支援：滑鼠 / 觸控 / 觸控筆 (Pointer Events)、雙指縮放平移
   ============================================================ */

function PixelView({ project, touchMode, rightPanelCollapsed, onToggleRightPanel }) {
  const [tool, setTool] = React.useState('pencil');
  const [color, setColor] = React.useState('#FFFFFFFF');
  const [symmetry, setSymmetry] = React.useState('none');
  const [showGrid, setShowGrid] = React.useState(true);
  const [zoom, setZoom] = React.useState(() => {
    const w = project?.width || 16;
    const h = project?.height || 16;
    return Math.max(1, Math.min(20, Math.floor(360 / Math.max(w, h))));
  });
  const [offset, setOffset] = React.useState({ x: 0, y: 0 });
  const [isDrawing, setIsDrawing] = React.useState(false);
  const [lastPos, setLastPos] = React.useState({ x: -1, y: -1 });
  const [shapeStart, setShapeStart] = React.useState(null);
  const [strokeStarted, setStrokeStarted] = React.useState(false);
  const [selection, setSelection] = React.useState(null);
  const [isPanning, setIsPanning] = React.useState(false);
  const [panStart, setPanStart] = React.useState(null);
  const [noiseAmount, setNoiseAmount] = React.useState(15);
  const [pressureEnabled, setPressureEnabled] = React.useState(false);
  const [pressureValue, setPressureValue] = React.useState(0.5);
  const [showConvertPanel, setShowConvertPanel] = React.useState(false);
  const [guiMode, setGuiMode] = React.useState(() => project?.type === 'gui' || !!AppState.guiMode);
  const [widgets, setWidgets] = React.useState(() => AppState.guiWidgets ? [...AppState.guiWidgets] : []);
  const [selectedWidgetId, setSelectedWidgetId] = React.useState(AppState.guiSelectedId || null);
  const [pendingGuiType, setPendingGuiType] = React.useState(null);
  const [cursor, setCursor] = React.useState({ x: 0, y: 0 });
  const guiDragRef = React.useRef(null);
  const widgetsCanvasRef = React.useRef(null);

  const canvasRef = React.useRef(null);
  const overlayRef = React.useRef(null);
  const containerRef = React.useRef(null);

  // Pointer 事件追蹤
  const pointersRef = React.useRef(new Map());
  const pinchRef = React.useRef(null);
  const activePointerRef = React.useRef(null);

  // 圖層狀態
  const [layers, setLayers] = React.useState(() => {
    if (AppState.layers && AppState.layers.length > 0) return AppState.layers;
    const canvas = createPixelCanvas(project.width, project.height);
    const defaultLayer = {
      id: uid(),
      name: '圖層 1',
      visible: true,
      locked: false,
      opacity: 1,
      alphaLock: false,
      texture: { canvas, name: '圖層 1' },
    };
    return [defaultLayer];
  });

  const [activeLayerId, setActiveLayerId] = React.useState(() => {
    return AppState.activeLayerId || layers[0]?.id;
  });

  const [, forceUpdate] = React.useReducer(x => x + 1, 0);

  React.useEffect(() => {
    PixelCanvasCore.tool = tool;
    PixelCanvasCore.primaryColor = color;
    PixelCanvasCore.symmetryMode = symmetry;
    PixelCanvasCore.zoom = zoom;
  }, [tool, color, symmetry, zoom]);

  // 繪製合成畫布
  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    canvas.width = project.width;
    canvas.height = project.height;
    PixelCanvasCore.drawLayers(canvas, layers, project.width, project.height);
    forceUpdate();
  }, [layers]);

  React.useEffect(() => {
    AppState.layers = layers;
    AppState.activeLayerId = activeLayerId;
    AppState.textures = {};
    layers.forEach(l => { AppState.textures[l.id] = l.texture; });
  }, [layers, activeLayerId]);

  React.useEffect(() => {
    AppState.guiWidgets = widgets;
    AppState.guiSelectedId = selectedWidgetId;
    AppState.guiMode = guiMode;
    const sel = widgets.find(w => w.id === selectedWidgetId);
    AppState.guiSelectionLabel = sel
      ? `${guiTypeLabel(sel.type)}  ${sel.x},${sel.y}  ${sel.w}×${sel.h}`
      : null;
    setState({});
  }, [widgets, selectedWidgetId, guiMode]);

  React.useEffect(() => {
    const canvas = widgetsCanvasRef.current;
    if (!canvas) return;
    canvas.width = project.width;
    canvas.height = project.height;
    const ctx = canvas.getContext('2d');
    ctx.imageSmoothingEnabled = false;
    drawGuiWidgets(ctx, widgets, selectedWidgetId, project.width, project.height);
  }, [widgets, selectedWidgetId, project.width, project.height, guiMode]);

  const activeLayer = layers.find(l => l.id === activeLayerId);

  // ---- 座標計算 ----
  const getPixelPos = (e) => {
    const canvas = canvasRef.current;
    const rect = canvas.getBoundingClientRect();
    const px = (e.clientX - rect.left) / rect.width * project.width;
    const py = (e.clientY - rect.top) / rect.height * project.height;
    return { x: Math.floor(px), y: Math.floor(py) };
  };

  // ---- 筆劃邏輯 ----
  const startStroke = (x, y, pressure = 0.5) => {
    const layer = activeLayer;
    if (!layer || layer.locked) return;
    pushUndo();

    if (tool === 'pencil' || tool === 'eraser') {
      const c = tool === 'eraser' ? '#00000000' : color;
      PixelCanvasCore.putPixel(layer, x, y, c, project.width, project.height);
      // 壓力感測：如果有壓力，畫出較粗的筆觸
      if (pressureEnabled && pressure > 0 && pressure !== 0.5) {
        const extra = Math.round(pressure * 2);
        if (extra > 0) {
          PixelCanvasCore.line(layer, x - extra, y, x + extra, y, c, project.width, project.height);
          PixelCanvasCore.line(layer, x, y - extra, x, y + extra, c, project.width, project.height);
        }
      }
      setStrokeStarted(true);
    } else if (tool === 'fill') {
      PixelCanvasCore.fill(layer, x, y, color, project.width, project.height);
      setStrokeStarted(true);
    } else if (tool === 'eyedropper') {
      const picked = PixelCanvasCore.eyedropper(layers, x, y, project.width, project.height);
      if (picked) setColor(picked);
    } else if (tool === 'line' || tool === 'rect' || tool === 'circle') {
      setShapeStart({ x, y });
    } else if (tool === 'select') {
      setShapeStart({ x, y });
    }
    setLastPos({ x, y });
  };

  const continueStroke = (x, y, pressure = 0.5) => {
    const layer = activeLayer;
    if (!layer || layer.locked) return;

    if (tool === 'pencil' || tool === 'eraser') {
      const c = tool === 'eraser' ? '#00000000' : color;
      PixelCanvasCore.line(layer, lastPos.x, lastPos.y, x, y, c, project.width, project.height);
      // 壓力感測：增加粗細
      if (pressureEnabled && pressure > 0 && pressure !== 0.5) {
        const extra = Math.max(0, Math.round(pressure * 2 - 1));
        if (extra > 0) {
          for (let i = 1; i <= extra; i++) {
            PixelCanvasCore.line(layer, lastPos.x, lastPos.y + i, x, y + i, c, project.width, project.height);
            PixelCanvasCore.line(layer, lastPos.x, lastPos.y - i, x, y - i, c, project.width, project.height);
          }
        }
      }
    } else if (tool === 'line' || tool === 'rect' || tool === 'circle' || tool === 'select') {
      drawOverlayPreview(shapeStart.x, shapeStart.y, x, y);
    }
    setLastPos({ x, y });
  };

  const endStroke = (x, y) => {
    const layer = activeLayer;
    if (!layer) return;

    if (tool === 'line') {
      PixelCanvasCore.line(layer, shapeStart.x, shapeStart.y, x, y, color, project.width, project.height);
      clearOverlay();
    } else if (tool === 'rect') {
      PixelCanvasCore.rect(layer, shapeStart.x, shapeStart.y, x, y, color, project.width, project.height);
      clearOverlay();
    } else if (tool === 'circle') {
      const dx = x - shapeStart.x;
      const dy = y - shapeStart.y;
      const r = Math.round(Math.sqrt(dx * dx + dy * dy));
      PixelCanvasCore.circle(layer, shapeStart.x, shapeStart.y, r, color, project.width, project.height);
      clearOverlay();
    } else if (tool === 'select') {
      const x1 = Math.min(shapeStart.x, x);
      const y1 = Math.min(shapeStart.y, y);
      const w = Math.abs(x - shapeStart.x) + 1;
      const h = Math.abs(y - shapeStart.y) + 1;
      setSelection({ x: x1, y: y1, w, h });
      clearOverlay();
    }

    setIsDrawing(false);
    setShapeStart(null);
    setStrokeStarted(false);
    setLayers([...layers]);
  };

  const drawOverlayPreview = (x0, y0, x1, y1) => {
    const overlay = overlayRef.current;
    if (!overlay) return;
    const ctx = overlay.getContext('2d');
    ctx.imageSmoothingEnabled = false;
    ctx.clearRect(0, 0, overlay.width, overlay.height);
    ctx.strokeStyle = color;
    ctx.lineWidth = 1;
    ctx.setLineDash([2, 2]);

    if (tool === 'line') {
      ctx.beginPath();
      ctx.moveTo(x0 + 0.5, y0 + 0.5);
      ctx.lineTo(x1 + 0.5, y1 + 0.5);
      ctx.stroke();
    } else if (tool === 'rect' || tool === 'select') {
      const x = Math.min(x0, x1);
      const y = Math.min(y0, y1);
      const w = Math.abs(x1 - x0) + 1;
      const h = Math.abs(y1 - y0) + 1;
      ctx.strokeRect(x + 0.5, y + 0.5, w, h);
    } else if (tool === 'circle') {
      const dx = x1 - x0;
      const dy = y1 - y0;
      const r = Math.round(Math.sqrt(dx * dx + dy * dy));
      ctx.beginPath();
      ctx.arc(x0 + 0.5, y0 + 0.5, r, 0, Math.PI * 2);
      ctx.stroke();
    }
  };

  const clearOverlay = () => {
    const overlay = overlayRef.current;
    if (!overlay) return;
    const ctx = overlay.getContext('2d');
    ctx.clearRect(0, 0, overlay.width, overlay.height);
  };

  // ---- Pointer Event 處理（統一滑鼠/觸控/觸控筆）----

  const handlePointerDown = (e) => {
    e.preventDefault();
    const canvas = canvasRef.current;
    if (!canvas) return;
    try { canvas.setPointerCapture(e.pointerId); } catch (err) {}

    const p = { id: e.pointerId, x: e.clientX, y: e.clientY };
    pointersRef.current.set(e.pointerId, p);

    // 雙指：進入 pinch 模式
    if (pointersRef.current.size === 2) {
      const pts = Array.from(pointersRef.current.values());
      const dx = pts[1].x - pts[0].x;
      const dy = pts[1].y - pts[0].y;
      const dist = Math.sqrt(dx * dx + dy * dy);
      const midX = (pts[0].x + pts[1].x) / 2;
      const midY = (pts[0].y + pts[1].y) / 2;
      pinchRef.current = { startDist: dist, startZoom: zoom, startOffset: { ...offset }, midX, midY };
      setIsDrawing(false);
      return;
    }

    // 單指：平移或繪圖
    if (e.button === 1 || (e.button === 0 && e.altKey) || tool === 'move') {
      setIsPanning(true);
      setPanStart({ x: e.clientX - offset.x, y: e.clientY - offset.y });
      return;
    }

    const pos = getPixelPos(e);
    setCursor({ x: pos.x, y: pos.y });
    if (pos.x < 0 || pos.y < 0 || pos.x >= project.width || pos.y >= project.height) return;

    if (guiMode) {
      const selected = widgets.find(w => w.id === selectedWidgetId);
      if (pendingGuiType) {
        placeGuiWidget(pendingGuiType, pos.x, pos.y);
        setPendingGuiType(null);
        return;
      }
      if (selected && !selected.locked) {
        const handle = hitTestHandle(selected, pos.x, pos.y, zoom);
        if (handle) {
          pushUndo();
          guiDragRef.current = { mode: 'resize', handle, id: selected.id };
          return;
        }
      }
      const hit = hitTestWidget(widgets, pos.x, pos.y);
      if (hit) {
        setSelectedWidgetId(hit.id);
        if (!hit.locked) {
          pushUndo();
          guiDragRef.current = {
            mode: 'move',
            id: hit.id,
            ox: pos.x - hit.x,
            oy: pos.y - hit.y,
          };
        }
        return;
      }
      setSelectedWidgetId(null);
      return;
    }

    activePointerRef.current = e.pointerId;
    setIsDrawing(true);

    // 更新壓力值顯示
    if (e.pressure !== undefined) {
      setPressureValue(e.pressure);
    }

    startStroke(pos.x, pos.y, e.pressure || 0.5);
  };

  const handlePointerMove = (e) => {
    e.preventDefault();

    // 更新指標位置
    if (pointersRef.current.has(e.pointerId)) {
      pointersRef.current.set(e.pointerId, { id: e.pointerId, x: e.clientX, y: e.clientY });
    }

    // 雙指縮放 + 平移
    if (pinchRef.current && pointersRef.current.size >= 2) {
      const pts = Array.from(pointersRef.current.values());
      const dx = pts[1].x - pts[0].x;
      const dy = pts[1].y - pts[0].y;
      const dist = Math.sqrt(dx * dx + dy * dy);
      const ratio = dist / pinchRef.current.startDist;
      const newZoom = Math.max(1, Math.min(128, Math.round(pinchRef.current.startZoom * ratio)));
      setZoom(newZoom);

      const midX = (pts[0].x + pts[1].x) / 2;
      const midY = (pts[0].y + pts[1].y) / 2;
      const dxMid = midX - pinchRef.current.midX;
      const dyMid = midY - pinchRef.current.midY;
      setOffset({
        x: pinchRef.current.startOffset.x + dxMid,
        y: pinchRef.current.startOffset.y + dyMid,
      });
      return;
    }

    if (isPanning) {
      setOffset({
        x: e.clientX - panStart.x,
        y: e.clientY - panStart.y,
      });
      return;
    }
    const pos = getPixelPos(e);
    setCursor({ x: pos.x, y: pos.y });
    if (guiDragRef.current) {
      const drag = guiDragRef.current;
      setWidgets(prev => prev.map(w => {
        if (w.id !== drag.id) return w;
        if (drag.mode === 'move') {
          return {
            ...w,
            x: Math.max(0, Math.min(project.width - w.w, Math.round(pos.x - drag.ox))),
            y: Math.max(0, Math.min(project.height - w.h, Math.round(pos.y - drag.oy))),
          };
        }
        if (drag.mode === 'resize') {
          return applyGuiResize(w, drag.handle, pos.x, pos.y, project.width, project.height);
        }
        return w;
      }));
      AppState.dirty = true;
      return;
    }
    if (!isDrawing) return;
    if (activePointerRef.current !== e.pointerId) return;
    if (pos.x === lastPos.x && pos.y === lastPos.y) return;

    // 更新壓力值
    if (e.pressure !== undefined) {
      setPressureValue(e.pressure);
    }

    continueStroke(pos.x, pos.y, e.pressure || 0.5);
    setLayers([...layers]);
  };

  const handlePointerUp = (e) => {
    e.preventDefault();
    pointersRef.current.delete(e.pointerId);

    if (pinchRef.current && pointersRef.current.size < 2) {
      pinchRef.current = null;
      if (pointersRef.current.size === 1) {
        setIsDrawing(false);
      }
      return;
    }

    if (isPanning) {
      setIsPanning(false);
      return;
    }
    if (guiDragRef.current) {
      guiDragRef.current = null;
      setWidgets(prev => [...prev]);
      return;
    }
    if (!isDrawing) return;
    if (activePointerRef.current !== e.pointerId) return;
    const pos = getPixelPos(e);
    endStroke(pos.x, pos.y);
    activePointerRef.current = null;
    setPressureValue(0.5);
  };

  const handlePointerCancel = (e) => {
    pointersRef.current.delete(e.pointerId);
    if (activePointerRef.current === e.pointerId && isDrawing) {
      const pos = lastPos;
      endStroke(pos.x, pos.y);
      activePointerRef.current = null;
    }
    if (pointersRef.current.size < 2) {
      pinchRef.current = null;
    }
    guiDragRef.current = null;
  };

  const handleWheel = (e) => {
    e.preventDefault();
    if (e.ctrlKey) {
      const delta = e.deltaY > 0 ? 0.8 : 1.25;
      setZoom(z => Math.max(1, Math.min(128, Math.round(z * delta))));
    } else {
      setOffset(o => ({
        x: o.x - e.deltaX,
        y: o.y - e.deltaY,
      }));
    }
  };

  // ---- 縮放控制 ----
  const zoomLevels = [1, 2, 4, 8, 10, 16, 20, 32, 40, 64, 80, 128];
  const zoomIn = () => setZoom(z => {
    const next = zoomLevels.find(x => x > z) || z;
    return next;
  });
  const zoomOut = () => setZoom(z => {
    const prev = [...zoomLevels].reverse().find(x => x < z) || z;
    return prev;
  });
  const fitView = () => {
    const container = containerRef.current;
    if (!container) return;
    const rect = container.getBoundingClientRect();
    const padding = 40;
    const availW = Math.max(100, rect.width - padding * 2);
    const availH = Math.max(100, rect.height - padding * 2);
    const z = Math.max(1, Math.floor(Math.min(availW / project.width, availH / project.height)));
    setZoom(z);
    setOffset({ x: 0, y: 0 });
  };

  React.useEffect(() => {
    const id = window.setTimeout(fitView, 50);
    return () => window.clearTimeout(id);
  }, [project.width, project.height]);

  // ---- 匯出 PNG ----
  const compositeCanvas = () => compositePixelAndGui(layers, widgets, project.width, project.height);

  const placeGuiWidget = (type, x, y) => {
    pushUndo();
    const wdg = createGuiWidget(type, x, y, project.width, project.height);
    if (type !== 'background') {
      wdg.x = Math.max(0, Math.min(project.width - wdg.w, Math.round(x - wdg.w / 2)));
      wdg.y = Math.max(0, Math.min(project.height - wdg.h, Math.round(y - wdg.h / 2)));
    }
    setWidgets(prev => [...prev, wdg]);
    setSelectedWidgetId(wdg.id);
    AppState.dirty = true;
    showToast(`已放置${guiTypeLabel(type)}`, 'success');
  };

  const updateSelectedWidget = (next) => {
    setWidgets(prev => prev.map(w => w.id === next.id ? next : w));
    AppState.dirty = true;
  };

  const duplicateSelectedWidget = () => {
    const src = widgets.find(w => w.id === selectedWidgetId);
    if (!src) return;
    pushUndo();
    const copy = {
      ...src,
      id: uid(),
      name: src.name + ' 副本',
      x: Math.min(project.width - src.w, src.x + 4),
      y: Math.min(project.height - src.h, src.y + 4),
    };
    setWidgets(prev => [...prev, copy]);
    setSelectedWidgetId(copy.id);
    AppState.dirty = true;
  };

  const deleteSelectedWidget = () => {
    if (!selectedWidgetId) return;
    pushUndo();
    setWidgets(prev => prev.filter(w => w.id !== selectedWidgetId));
    setSelectedWidgetId(null);
    AppState.dirty = true;
  };

  const alignSelected = (dir) => {
    const src = widgets.find(w => w.id === selectedWidgetId);
    if (!src) return;
    pushUndo();
    const next = alignGuiWidget(src, dir, project.width, project.height);
    setWidgets(prev => prev.map(w => w.id === src.id ? next : w));
    AppState.dirty = true;
  };

  const exportPNG = () => {
    const canvas = compositeCanvas();
    canvasToBlob(canvas).then(blob => {
      downloadBlob(blob, `${project.name}.png`);
      showToast('PNG 已匯出', 'success');
    });
  };

  const exportGuiPack = async () => {
    if (typeof JSZip === 'undefined') {
      showToast('JSZip 未載入，請稍後重試', 'error');
      return;
    }
    showToast('正在打包 GUI 資源包...', 'info');
    try {
      const zip = new JSZip();
      const name = (project?.name || 'gui').replace(/\s+/g, '_').toLowerCase();
      zip.file('pack.mcmeta', JSON.stringify({
        pack: {
          pack_format: packFormatFor(project?.mcVersion),
          description: `${project?.name || 'GUI'} · MC ${project?.mcVersion || '26.2'} · MC Studio`,
        },
      }, null, 2));
      const blob = await canvasToBlob(compositeCanvas());
      zip.file(`assets/minecraft/textures/gui/${name}.png`, blob);
      const content = await zip.generateAsync({ type: 'blob' });
      downloadBlob(content, `${name}_gui_pack.zip`);
      showToast('已匯出到 assets/minecraft/textures/gui/', 'success');
    } catch (err) {
      showToast('匯出失敗：' + err.message, 'error');
    }
  };

  // ---- 匯入圖片（一般匯入，不作轉換）----
  React.useEffect(() => {
    window.handleImageImport = async (file) => {
      try {
        const canvas = await loadImageToCanvas(file);
        pushUndo();
        const newLayer = {
          id: uid(),
          name: file.name.replace(/\.[^/.]+$/, ''),
          visible: true,
          locked: false,
          opacity: 1,
          alphaLock: false,
          texture: { canvas, name: file.name },
        };
        setLayers(prev => [newLayer, ...prev]);
        setActiveLayerId(newLayer.id);
        showToast(`已匯入 ${file.name}`, 'success');
      } catch (e) {
        showToast('匯入失敗：' + e.message, 'error');
      }
    };
    return () => { delete window.handleImageImport; };
  }, []);

  // ---- 圖片轉像素（開啟面板）----
  React.useEffect(() => {
    window.openPixelConvert = () => {
      setShowConvertPanel(true);
    };
    return () => { delete window.openPixelConvert; };
  }, []);

  // ---- 全域快捷鍵 ----
  React.useEffect(() => {
    window.zoomIn = zoomIn;
    window.zoomOut = zoomOut;
    window.resetView = fitView;
    window.toggleGrid = () => setShowGrid(v => !v);
    window.triggerExport = exportPNG;
    window.exportResourcePack = exportGuiPack;
    window.setSymmetry = setSymmetry;
    window.applyShading = applyShading;
    window.applyNoise = applyNoise;
    window.triggerCanvasResize = () => {
      // Canvas 本身是繪圖尺寸，不需要 resize；顯示尺寸由 CSS 控制
      // 但需要重新計算符合視圖的位置
      // forceUpdate 讓 React 重新計算樣式
    };
    return () => {
      window.zoomIn = null;
      window.zoomOut = null;
      window.resetView = null;
      window.toggleGrid = null;
      window.triggerExport = null;
      window.exportResourcePack = null;
      window.setSymmetry = null;
      window.applyShading = null;
      window.applyNoise = null;
      window.triggerCanvasResize = null;
    };
  }, [exportPNG]);

  // ---- 鍵盤快捷鍵 ----
  React.useEffect(() => {
    const handler = (e) => {
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') return;
      const cmd = e.ctrlKey || e.metaKey;
      if (cmd && e.key === 'z' && !e.shiftKey) {
        e.preventDefault();
        undo();
        setLayers([...(AppState.layers || layers)]);
        setWidgets([...(AppState.guiWidgets || [])]);
        setSelectedWidgetId(AppState.guiSelectedId || null);
      } else if (cmd && (e.key === 'Z' || (e.shiftKey && e.key === 'z'))) {
        e.preventDefault();
        redo();
        setLayers([...(AppState.layers || layers)]);
        setWidgets([...(AppState.guiWidgets || [])]);
        setSelectedWidgetId(AppState.guiSelectedId || null);
      } else if (cmd && e.key === 's') {
        e.preventDefault();
        showToast('專案已儲存（本機）', 'success');
      } else if (cmd && e.key === 'e') {
        e.preventDefault();
        exportPNG();
      } else if (e.key === 'b' || e.key === 'B') { setTool('pencil'); }
      else if (e.key === 'e' || e.key === 'E') { if (!cmd) setTool('eraser'); }
      else if (e.key === 'g' || e.key === 'G') { setTool('fill'); }
      else if (e.key === 'i' || e.key === 'I') { setTool('eyedropper'); }
      else if (e.key === 'l' || e.key === 'L') { setTool('line'); }
      else if (e.key === 'r' || e.key === 'R') { setTool('rect'); }
      else if (e.key === 'c' || e.key === 'C') { if (!cmd) setTool('circle'); }
      else if (e.key === 'm' || e.key === 'M') { setTool('select'); }
      else if (e.key === 'v' || e.key === 'V') { if (!cmd) setTool('move'); }
      else if (e.key === '+' || e.key === '=') { zoomIn(); }
      else if (e.key === '-' || e.key === '_') { zoomOut(); }
      else if (e.key === '0') { fitView(); }
      else if (e.key === '1') { setZoom(1); }
      else if (guiMode && cmd && (e.key === 'd' || e.key === 'D')) {
        e.preventDefault();
        duplicateSelectedWidget();
      } else if (guiMode && (e.key === 'Delete' || e.key === 'Backspace')) {
        e.preventDefault();
        deleteSelectedWidget();
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [layers, zoom, guiMode, widgets, selectedWidgetId]);

  // ---- 陰影 / 外框 / 雜訊 ----
  const applyOutline = () => {
    if (!activeLayer) return;
    pushUndo();
    PixelCanvasCore.applyOutline(activeLayer.texture.canvas, color, '1px');
    setLayers([...layers]);
    showToast('已套用 1px 外框', 'success');
  };

  const applyNoise = () => {
    if (!activeLayer) return;
    pushUndo();
    PixelCanvasCore.applyNoise(activeLayer.texture.canvas, noiseAmount);
    setLayers([...layers]);
    showToast('已套用像素雜訊', 'success');
  };

  const applyShading = (mode) => {
    if (!activeLayer) return;
    pushUndo();
    PixelCanvasCore.applyShading(activeLayer.texture.canvas, mode, color);
    setLayers([...layers]);
    const labels = { highlight: '高光', midtone: '中間調', shadow: '陰影', deep: '深影' };
    showToast(`已套用 ${labels[mode] || mode}`, 'success');
  };

  const applyMirror = () => {
    if (!activeLayer) return;
    pushUndo();
    PixelCanvasCore.mirrorHorizontal(activeLayer.texture.canvas, project.width, project.height);
    setLayers([...layers]);
    showToast('已水平鏡像', 'success');
  };

  const applyRotate = () => {
    if (!activeLayer) return;
    pushUndo();
    PixelCanvasCore.rotate90(activeLayer.texture.canvas, project.width, project.height);
    setLayers([...layers]);
    showToast('已旋轉 90°', 'success');
  };

  // ---- 顯示尺寸計算 ----
  const displayWidth = project.width * zoom;
  const displayHeight = project.height * zoom;

  // ---- 渲染 ----
  return React.createElement('div', {
    className: 'view-content',
    style: { position: 'absolute', inset: 0, display: 'flex', width: '100%', height: '100%' },
  },
    // 主畫布區域
    React.createElement('div', {
      className: 'main-area',
      ref: containerRef,
      onPointerDown: handlePointerDown,
      onPointerMove: handlePointerMove,
      onPointerUp: handlePointerUp,
      onPointerCancel: handlePointerCancel,
      onPointerLeave: handlePointerCancel,
      onWheel: handleWheel,
      onDragOver: (e) => {
        if (e.dataTransfer.types.includes('text/gui-type')) {
          e.preventDefault();
          e.dataTransfer.dropEffect = 'copy';
        }
      },
      onDrop: (e) => {
        const type = e.dataTransfer.getData('text/gui-type');
        if (!type) return;
        e.preventDefault();
        setGuiMode(true);
        const pos = getPixelPos(e);
        placeGuiWidget(type, pos.x, pos.y);
      },
      style: { touchAction: 'none' },
    },
      React.createElement('div', { className: 'canvas-container' },
        React.createElement('div', {
          className: 'canvas-wrapper',
          style: {
            width: displayWidth,
            height: displayHeight,
            transform: `translate(${offset.x}px, ${offset.y}px)`,
            cursor: isPanning ? 'grabbing' : (tool === 'eyedropper' ? 'copy' : tool === 'move' ? 'grab' : 'crosshair'),
          },
        },
          React.createElement('canvas', {
            ref: canvasRef,
            width: project.width,
            height: project.height,
            style: {
              width: displayWidth,
              height: displayHeight,
              imageRendering: 'pixelated',
              display: 'block',
            },
          }),
          React.createElement('canvas', {
            ref: overlayRef,
            width: project.width,
            height: project.height,
            className: 'canvas-grid-overlay',
            style: {
              width: displayWidth,
              height: displayHeight,
              imageRendering: 'pixelated',
            },
          }),
          React.createElement('canvas', {
            ref: widgetsCanvasRef,
            width: project.width,
            height: project.height,
            className: 'gui-widget-overlay',
            style: {
              width: displayWidth,
              height: displayHeight,
              imageRendering: 'pixelated',
              pointerEvents: 'none',
            },
          }),
          // 像素格線
          showGrid && zoom >= 4 && React.createElement('div', {
            style: {
              position: 'absolute',
              inset: 0,
              pointerEvents: 'none',
              backgroundImage: `
                linear-gradient(to right, rgba(255,255,255,0.08) 1px, transparent 1px),
                linear-gradient(to bottom, rgba(255,255,255,0.08) 1px, transparent 1px)
              `,
              backgroundSize: `${zoom}px ${zoom}px`,
            },
          }),
        ),
      ),
      // 縮放控制
      React.createElement('div', { className: 'zoom-controls' },
        React.createElement('button', { className: 'zoom-btn', onClick: zoomOut, title: '縮小 (-)' }, '−'),
        React.createElement('span', { className: 'zoom-level' }, `${zoom}x`),
        React.createElement('button', { className: 'zoom-btn', onClick: zoomIn, title: '放大 (+)' }, '+'),
        React.createElement('button', {
          className: 'zoom-btn', onClick: fitView, title: '符合畫面 (0)',
          style: { fontSize: '10px', fontWeight: 400 }
        }, '適合'),
      ),

      React.createElement('div', { className: 'gui-hud' },
        React.createElement('span', null, `${Math.max(0, cursor.x)}, ${Math.max(0, cursor.y)}`),
        selectedWidgetId && widgets.find(w => w.id === selectedWidgetId)
          ? React.createElement('span', null, (() => {
            const s = widgets.find(w => w.id === selectedWidgetId);
            return `${guiTypeLabel(s.type)}  ${s.x},${s.y}  ${s.w}×${s.h}` + (s.type === 'progress' ? `  ${s.percent}%` : '');
          })())
          : React.createElement('span', null, guiMode ? (pendingGuiType ? `放置${guiTypeLabel(pendingGuiType)}` : 'GUI 模式') : '像素繪製'),
      ),

      // 觸控模式底部工具列
      touchMode && React.createElement('div', { className: 'mobile-toolbar' },
        ['pencil', 'eraser', 'fill', 'eyedropper', 'line', 'rect', 'circle'].map(t =>
          React.createElement('button', {
            key: t,
            className: 'tool-btn' + (tool === t ? ' active' : ''),
            onClick: () => setTool(t),
            title: t,
          },
            React.createElement(SvgIcon, {
              name: { pencil: 'Pencil', eraser: 'Eraser', fill: 'Fill', eyedropper: 'Eyedropper', line: 'Line', rect: 'Rect', circle: 'Circle' }[t]
            }),
          )
        ),
      ),
    ),

    // 右側面板切換按鈕（收合時顯示）
    rightPanelCollapsed && React.createElement('button', {
      className: 'panel-toggle-btn right-panel-toggle',
      onClick: onToggleRightPanel,
      title: '展開右側面板',
    }, '◀'),

    // 右側面板
    !rightPanelCollapsed && React.createElement('div', { className: 'right-panel' },
      React.createElement('div', { className: 'panel-section' },
        React.createElement('div', { className: 'panel-section__header' },
          React.createElement('span', null, '工作區模式'),
        ),
        React.createElement('div', { className: 'panel-section__body' },
          React.createElement('div', { className: 'gui-mode-toggle' },
            React.createElement('button', {
              className: 'mc-helper-btn' + (!guiMode ? ' active' : ''),
              onClick: () => { setGuiMode(false); setPendingGuiType(null); },
            }, '像素繪製'),
            React.createElement('button', {
              className: 'mc-helper-btn' + (guiMode ? ' active' : ''),
              onClick: () => setGuiMode(true),
            }, 'GUI 模式'),
          ),
        ),
      ),
      guiMode && window.GuiLibraryPanel && React.createElement(window.GuiLibraryPanel, {
        pendingType: pendingGuiType,
        onPick: (type) => {
          setGuiMode(true);
          setPendingGuiType(type);
          showToast(`點畫布放置「${guiTypeLabel(type)}」`, 'info');
        },
        selected: widgets.find(w => w.id === selectedWidgetId) || null,
        onChange: updateSelectedWidget,
        widgets,
        selectedId: selectedWidgetId,
        onSelect: setSelectedWidgetId,
        onReorder: (next) => { pushUndo(); setWidgets(next); AppState.dirty = true; },
        onAlign: alignSelected,
        onDuplicate: duplicateSelectedWidget,
        onDelete: deleteSelectedWidget,
        canvasW: project.width,
        canvasH: project.height,
      }),
      !guiMode && React.createElement(ColorPickerPanel, { color, onColorChange: setColor }),
      // 壓力感測（若有壓力輸入則顯示）
      pressureEnabled && React.createElement('div', { className: 'panel-section' },
        React.createElement('div', { className: 'panel-section__header' },
          React.createElement('span', null, '壓力感測'),
        ),
        React.createElement('div', { className: 'panel-section__body' },
          React.createElement('div', { className: 'pressure-indicator' },
            React.createElement('span', { style: { fontSize: '10px', color: 'var(--text-tertiary)' } }, '目前壓力'),
            React.createElement('div', { className: 'pressure-bar' },
              React.createElement('div', {
                className: 'pressure-bar__fill',
                style: { width: `${Math.round(pressureValue * 100)}%` }
              }),
            ),
            React.createElement('span', { style: { fontSize: '10px', color: 'var(--text-tertiary)', minWidth: '30px', textAlign: 'right', fontFamily: 'var(--font-mono)' } },
              Math.round(pressureValue * 100) + '%'
            ),
          ),
        ),
      ),
      // MC 像素工具
      React.createElement('div', { className: 'panel-section' },
        React.createElement('div', { className: 'panel-section__header' },
          React.createElement('span', null, 'MC 像素工具'),
        ),
        React.createElement('div', { className: 'panel-section__body' },
          React.createElement('div', { className: 'mc-helpers' },
            React.createElement('div', { className: 'mc-helpers__group' },
              React.createElement('div', { className: 'mc-helpers__title' }, '對稱模式'),
              React.createElement('div', { className: 'mc-helpers__row' },
                ['none', 'horizontal', 'vertical', 'both'].map(m =>
                  React.createElement('button', {
                    key: m,
                    className: 'mc-helper-btn' + (symmetry === m ? ' active' : ''),
                    onClick: () => setSymmetry(m),
                  }, m === 'none' ? '關閉' : m === 'horizontal' ? '水平' : m === 'vertical' ? '垂直' : '雙向')
                ),
              ),
            ),
            React.createElement('div', { className: 'mc-helpers__group' },
              React.createElement('div', { className: 'mc-helpers__title' }, '特效'),
              React.createElement('div', { className: 'mc-helpers__row' },
                React.createElement('button', { className: 'mc-helper-btn', onClick: applyOutline, title: '以目前顏色繪製 1px 外框' }, '外框'),
                React.createElement('button', { className: 'mc-helper-btn', onClick: applyMirror, title: '水平鏡像翻轉' }, '鏡像'),
                React.createElement('button', { className: 'mc-helper-btn', onClick: applyRotate, title: '順時針旋轉 90 度' }, '旋轉'),
              ),
              React.createElement('div', { className: 'mc-helpers__title', style: { marginTop: '6px' } }, '陰影'),
              React.createElement('div', { className: 'mc-helpers__row' },
                React.createElement('button', { className: 'mc-helper-btn', onClick: () => applyShading('highlight'), title: '高光（變亮）' }, '高光'),
                React.createElement('button', { className: 'mc-helper-btn', onClick: () => applyShading('midtone'), title: '中間調' }, '中調'),
                React.createElement('button', { className: 'mc-helper-btn', onClick: () => applyShading('shadow'), title: '陰影（變暗）' }, '陰影'),
                React.createElement('button', { className: 'mc-helper-btn', onClick: () => applyShading('deep'), title: '深影' }, '深影'),
              ),
            ),
            React.createElement('div', { className: 'mc-helpers__group' },
              React.createElement('div', { className: 'mc-helpers__title' }, '像素雜訊'),
              React.createElement('div', { className: 'mc-helpers__row' },
                React.createElement('button', {
                  className: 'mc-helper-btn',
                  style: { flex: 'none', width: '100%' },
                  onClick: applyNoise,
                }, '套用雜訊'),
              ),
              React.createElement('div', { className: 'slider-row' },
                React.createElement('span', { style: { fontSize: '10px', color: 'var(--text-tertiary)', width: '48px' } }, '強度'),
                React.createElement('input', {
                  type: 'range', min: 1, max: 60, value: noiseAmount,
                  onChange: (e) => setNoiseAmount(parseInt(e.target.value)),
                }),
                React.createElement('span', { className: 'slider-value' }, noiseAmount),
              ),
            ),
            React.createElement('div', { className: 'mc-helpers__group' },
              React.createElement('div', { className: 'mc-helpers__title' }, '圖片轉像素'),
              React.createElement('button', {
                className: 'mc-helper-btn',
                style: { width: '100%' },
                onClick: () => setShowConvertPanel(true),
              },
                React.createElement(SvgIcon, { name: 'Image', size: 10 }),
                ' 開啟轉換工具',
              ),
            ),
            React.createElement('div', { className: 'mc-helpers__group' },
              React.createElement('div', { className: 'mc-helpers__title' }, '顯示'),
              React.createElement('div', { className: 'toggle-row' },
                React.createElement('span', { className: 'toggle-label' }, '像素格線'),
                React.createElement('div', {
                  className: 'toggle-switch' + (showGrid ? ' on' : ''),
                  onClick: () => setShowGrid(v => !v),
                }),
              ),
              React.createElement('div', { className: 'toggle-row' },
                React.createElement('span', { className: 'toggle-label' }, '壓力感測筆'),
                React.createElement('div', {
                  className: 'toggle-switch' + (pressureEnabled ? ' on' : ''),
                  onClick: () => setPressureEnabled(v => !v),
                }),
              ),
            ),
          ),
        ),
      ),
      React.createElement(MCToolsPanel, { view: 'pixel', project, onExport: exportPNG }),
      React.createElement(LayersPanel, {
        layers,
        activeLayerId,
        onLayersChange: (newLayers) => { setLayers(newLayers); },
        onActiveLayerChange: setActiveLayerId,
      }),
    ),

    // 圖片轉像素對話框
    showConvertPanel && React.createElement(PixelConvertDialog, {
      project,
      onClose: () => setShowConvertPanel(false),
      onApply: (resultCanvas, name) => {
        pushUndo();
        const newLayer = {
          id: uid(),
          name: name || '轉換結果',
          visible: true,
          locked: false,
          opacity: 1,
          alphaLock: false,
          texture: { canvas: resultCanvas, name: name || '轉換結果' },
        };
        setLayers(prev => [newLayer, ...prev]);
        setActiveLayerId(newLayer.id);
        setShowConvertPanel(false);
        showToast('圖片已轉換為像素圖', 'success');
      },
    }),
  );
}

// ============================================================
// 圖片轉像素對話框
// ============================================================
function PixelConvertDialog({ project, onClose, onApply }) {
  const [sourceImage, setSourceImage] = React.useState(null);
  const [sourceName, setSourceName] = React.useState('');
  const [targetWidth, setTargetWidth] = React.useState(project.width);
  const [targetHeight, setTargetHeight] = React.useState(project.height);
  const [useCanvasSize, setUseCanvasSize] = React.useState(true);
  const [colorCount, setColorCount] = React.useState(32);
  const [ditherEnabled, setDitherEnabled] = React.useState(true);
  const [ditherStrength, setDitherStrength] = React.useState(50);
  const [outlineEnabled, setOutlineEnabled] = React.useState(false);
  const [paletteMode, setPaletteMode] = React.useState('auto'); // 'auto' | 'minecraft'
  const [resultCanvas, setResultCanvas] = React.useState(null);

  const fileInputRef = React.useRef(null);
  const originalRef = React.useRef(null);
  const previewRef = React.useRef(null);

  const handleFileSelect = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      const img = new Image();
      img.onload = () => {
        setSourceImage(img);
        setSourceName(file.name.replace(/\.[^/.]+$/, ''));
        // 根據原始比例設定建議的目標尺寸
        if (useCanvasSize) {
          setTargetWidth(project.width);
          setTargetHeight(project.height);
        }
        // 延遲到下一幀計算
        setTimeout(() => updatePreview(), 0);
      };
      img.src = ev.target.result;
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  // 更新預覽
  const updatePreview = React.useCallback(() => {
    if (!sourceImage) return;
    const tw = targetWidth;
    const th = targetHeight;

    // Step 1: 降採樣到目標尺寸
    const offscreen = document.createElement('canvas');
    offscreen.width = tw;
    offscreen.height = th;
    const ctx = offscreen.getContext('2d');
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';
    ctx.drawImage(sourceImage, 0, 0, tw, th);

    let imageData = ctx.getImageData(0, 0, tw, th);
    let data = imageData.data;

    // Step 2: 顏色量化
    const palette = paletteMode === 'minecraft' ? MINECRAFT_PALETTE : null;
    let quantizedData;

    if (paletteMode === 'minecraft') {
      // 對齊 Minecraft 色板
      quantizedData = quantizeToPalette(data, palette);
    } else {
      // 自動顏色量化（中位數切割的簡化版：平均分割）
      quantizedData = quantizeAuto(data, colorCount);
    }

    // Step 3: 抖動（Floyd-Steinberg 或有序抖動）
    if (ditherEnabled && ditherStrength > 0) {
      quantizedData = applyOrderedDither(data, tw, th, colorCount, ditherStrength / 100);
      if (paletteMode === 'minecraft') {
        quantizedData = quantizeToPalette(quantizedData, palette);
      }
    }

    // Step 4: 外框（邊緣偵測 + 深色邊）
    if (outlineEnabled) {
      quantizedData = addPixelOutline(quantizedData, tw, th);
    }

    // 寫回
    const outCanvas = document.createElement('canvas');
    outCanvas.width = tw;
    outCanvas.height = th;
    const outCtx = outCanvas.getContext('2d');
    outCtx.putImageData(new ImageData(quantizedData, tw, th), 0, 0);

    // 更新預覽畫布
    if (previewRef.current) {
      previewRef.current.width = tw;
      previewRef.current.height = th;
      const pctx = previewRef.current.getContext('2d');
      pctx.imageSmoothingEnabled = false;
      pctx.clearRect(0, 0, tw, th);
      pctx.drawImage(outCanvas, 0, 0);
    }

    setResultCanvas(outCanvas);

    // 更新原圖預覽（縮小到目標尺寸，方便對比）
    if (originalRef.current) {
      originalRef.current.width = tw;
      originalRef.current.height = th;
      const octx = originalRef.current.getContext('2d');
      octx.imageSmoothingEnabled = true;
      octx.clearRect(0, 0, tw, th);
      octx.drawImage(sourceImage, 0, 0, tw, th);
    }
  }, [sourceImage, targetWidth, targetHeight, colorCount, ditherEnabled, ditherStrength, outlineEnabled, paletteMode]);

  React.useEffect(() => {
    if (sourceImage) updatePreview();
  }, [targetWidth, targetHeight, colorCount, ditherEnabled, ditherStrength, outlineEnabled, paletteMode, sourceImage, updatePreview]);

  const colorCountOptions = [8, 16, 32, 64, 128, 256];

  return React.createElement('div', {
    className: 'modal-overlay',
    onClick: (e) => e.target.className === 'modal-overlay' && onClose(),
  },
    React.createElement('div', { className: 'modal', style: { width: '600px', maxWidth: '92vw' } },
      React.createElement('div', { className: 'modal__header' },
        React.createElement('span', { className: 'modal__title' }, '圖片轉像素圖'),
        React.createElement('button', { className: 'modal__close', onClick: onClose },
          React.createElement(SvgIcon, { name: 'X', size: 14 }),
        ),
      ),
      React.createElement('div', { className: 'modal__body', style: { display: 'flex', flexDirection: 'column', gap: '12px' } },
        // 上傳區
        !sourceImage
          ? React.createElement('div', {
            style: {
              display: 'flex', flexDirection: 'column', alignItems: 'center',
              justifyContent: 'center', padding: '40px',
              border: '2px dashed var(--border-strong)', borderRadius: 'var(--radius-md)',
              color: 'var(--text-tertiary)', cursor: 'pointer',
            },
            onClick: () => fileInputRef.current?.click(),
          },
            React.createElement(SvgIcon, { name: 'Image', size: 32, style: { marginBottom: '8px', opacity: 0.5 } }),
            React.createElement('div', { style: { fontSize: '12px' } }, '點擊選擇圖片或拖曳到此'),
            React.createElement('div', { style: { fontSize: '10px', color: 'var(--text-muted)', marginTop: '4px' } },
              '支援 PNG / JPG / WEBP'
            ),
          )
          : React.createElement(React.Fragment, null,
            // 預覽對比
            React.createElement('div', { className: 'pixel-convert-preview' },
              React.createElement('div', { className: 'pixel-convert-preview__box' },
                React.createElement('span', { className: 'pixel-convert-preview__label' }, '原圖'),
                React.createElement('canvas', { ref: originalRef }),
              ),
              React.createElement('div', { className: 'pixel-convert-preview__box' },
                React.createElement('span', { className: 'pixel-convert-preview__label' }, '結果'),
                React.createElement('canvas', { ref: previewRef }),
              ),
            ),
            // 設定區
            React.createElement('div', { className: 'pixel-convert-settings' },
              // 目標尺寸
              React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, '目標尺寸'),
                React.createElement('div', { style: { display: 'flex', gap: '6px', alignItems: 'center' } },
                  React.createElement('button', {
                    className: 'mc-helper-btn' + (useCanvasSize ? ' active' : ''),
                    style: { padding: '2px 8px', fontSize: '10px' },
                    onClick: () => {
                      setUseCanvasSize(true);
                      setTargetWidth(project.width);
                      setTargetHeight(project.height);
                    },
                  }, `畫布 (${project.width}×${project.height})`),
                  React.createElement('button', {
                    className: 'mc-helper-btn' + (!useCanvasSize ? ' active' : ''),
                    style: { padding: '2px 8px', fontSize: '10px' },
                    onClick: () => setUseCanvasSize(false),
                  }, '自訂'),
                ),
              ),
              !useCanvasSize && React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, '自訂尺寸'),
                React.createElement('div', { style: { display: 'flex', gap: '6px', alignItems: 'center' } },
                  React.createElement('input', {
                    type: 'number', className: 'form-input', style: { width: '60px' },
                    value: targetWidth, min: 1, max: 512,
                    onChange: (e) => setTargetWidth(Math.max(1, Math.min(512, parseInt(e.target.value) || 1))),
                  }),
                  ' × ',
                  React.createElement('input', {
                    type: 'number', className: 'form-input', style: { width: '60px' },
                    value: targetHeight, min: 1, max: 512,
                    onChange: (e) => setTargetHeight(Math.max(1, Math.min(512, parseInt(e.target.value) || 1))),
                  }),
                ),
              ),
              // 顏色模式
              React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, '色板'),
                React.createElement('div', { style: { display: 'flex', gap: '4px' } },
                  React.createElement('button', {
                    className: 'mc-helper-btn' + (paletteMode === 'auto' ? ' active' : ''),
                    style: { padding: '2px 8px', fontSize: '10px' },
                    onClick: () => setPaletteMode('auto'),
                  }, '自動量化'),
                  React.createElement('button', {
                    className: 'mc-helper-btn' + (paletteMode === 'minecraft' ? ' active' : ''),
                    style: { padding: '2px 8px', fontSize: '10px' },
                    onClick: () => setPaletteMode('minecraft'),
                  }, 'MC 色板'),
                ),
              ),
              // 顏色數量
              paletteMode === 'auto' && React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, '顏色數量'),
                React.createElement('div', { style: { display: 'flex', gap: '4px', flexWrap: 'wrap', justifyContent: 'flex-end' } },
                  colorCountOptions.map(n =>
                    React.createElement('button', {
                      key: n,
                      className: 'mc-helper-btn' + (colorCount === n ? ' active' : ''),
                      style: { padding: '2px 8px', fontSize: '10px', minWidth: '36px' },
                      onClick: () => setColorCount(n),
                    }, n)
                  ),
                ),
              ),
              // 抖動
              React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, '抖動'),
                React.createElement('div', { className: 'toggle-switch' + (ditherEnabled ? ' on' : ''), onClick: () => setDitherEnabled(v => !v) }),
              ),
              ditherEnabled && React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, '抖動強度'),
                React.createElement('input', {
                  type: 'range', min: 10, max: 100, value: ditherStrength,
                  onChange: (e) => setDitherStrength(parseInt(e.target.value)),
                  style: { flex: 1, maxWidth: '200px' },
                }),
                React.createElement('span', { className: 'slider-value' }, `${ditherStrength}%`),
              ),
              // 外框
              React.createElement('div', { className: 'pixel-convert-row' },
                React.createElement('label', null, 'MC 風格外框'),
                React.createElement('div', { className: 'toggle-switch' + (outlineEnabled ? ' on' : ''), onClick: () => setOutlineEnabled(v => !v) }),
              ),
            ),
            // 重新選擇檔案
            React.createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', fontSize: '11px' } },
              React.createElement('button', {
                className: 'mc-helper-btn',
                style: { padding: '2px 8px', fontSize: '10px' },
                onClick: () => fileInputRef.current?.click(),
              }, '更換圖片'),
            ),
          ),

        React.createElement('input', {
          ref: fileInputRef,
          type: 'file',
          accept: 'image/png,image/jpeg,image/webp',
          style: { display: 'none' },
          onChange: handleFileSelect,
        }),
      ),
      React.createElement('div', { className: 'modal__footer' },
        React.createElement('button', { className: 'btn btn--secondary', onClick: onClose }, '取消'),
        React.createElement('button', {
          className: 'btn btn--primary',
          disabled: !resultCanvas,
          onClick: () => resultCanvas && onApply(resultCanvas, `${sourceName}_像素化`),
        },
          React.createElement(SvgIcon, { name: 'Plus', size: 12 }),
          ' 新增為圖層',
        ),
      ),
    ),
  );
}

// ============================================================
// 顏色量化與圖像處理函式
// ============================================================

// 自動顏色量化（中位數切割精簡版）
function quantizeAuto(data, targetColors) {
  const pixels = [];
  for (let i = 0; i < data.length; i += 4) {
    if (data[i + 3] < 128) continue;
    pixels.push([data[i], data[i + 1], data[i + 2]]);
  }
  if (pixels.length === 0) return data;

  // 中位數切割
  const buckets = [pixels];
  while (buckets.length < targetColors) {
    // 找到範圍最大的 bucket
    let maxRangeIdx = -1;
    let maxRange = -1;
    for (let bi = 0; bi < buckets.length; bi++) {
      const b = buckets[bi];
      if (b.length < 2) continue;
      let rMin = 255, rMax = 0, gMin = 255, gMax = 0, bMin = 255, bMax = 0;
      for (const p of b) {
        if (p[0] < rMin) rMin = p[0]; if (p[0] > rMax) rMax = p[0];
        if (p[1] < gMin) gMin = p[1]; if (p[1] > gMax) gMax = p[1];
        if (p[2] < bMin) bMin = p[2]; if (p[2] > bMax) bMax = p[2];
      }
      const range = (rMax - rMin) + (gMax - gMin) + (bMax - bMin);
      if (range > maxRange) { maxRange = range; maxRangeIdx = bi; }
    }
    if (maxRangeIdx === -1) break;
    const bucket = buckets[maxRangeIdx];
    if (bucket.length < 2) break;
    // 找到變化最大的通道
    let rMin = 255, rMax = 0, gMin = 255, gMax = 0, bMin = 255, bMax = 0;
    for (const p of bucket) {
      if (p[0] < rMin) rMin = p[0]; if (p[0] > rMax) rMax = p[0];
      if (p[1] < gMin) gMin = p[1]; if (p[1] > gMax) gMax = p[1];
      if (p[2] < bMin) bMin = p[2]; if (p[2] > bMax) bMax = p[2];
    }
    const channel = (rMax - rMin >= gMax - gMin && rMax - rMin >= bMax - bMin) ? 0
      : (gMax - gMin >= bMax - bMin) ? 1 : 2;
    // 排序
    bucket.sort((a, b) => a[channel] - b[channel]);
    const mid = Math.floor(bucket.length / 2);
    const left = bucket.slice(0, mid);
    const right = bucket.slice(mid);
    buckets.splice(maxRangeIdx, 1, left, right);
  }

  // 計算每個 bucket 的平均色
  const palette = buckets.map(bucket => {
    let r = 0, g = 0, b = 0;
    for (const p of bucket) { r += p[0]; g += p[1]; b += p[2]; }
    return [Math.round(r / bucket.length), Math.round(g / bucket.length), Math.round(b / bucket.length)];
  });

  // 對映回每個像素
  const out = new Uint8ClampedArray(data);
  for (let i = 0; i < data.length; i += 4) {
    if (data[i + 3] < 128) { out[i + 3] = data[i + 3]; continue; }
    const r = data[i], g = data[i + 1], b = data[i + 2];
    let bestIdx = 0, bestDist = Infinity;
    for (let pi = 0; pi < palette.length; pi++) {
      const dr = r - palette[pi][0];
      const dg = g - palette[pi][1];
      const db = b - palette[pi][2];
      const d = dr * dr + dg * dg + db * db;
      if (d < bestDist) { bestDist = d; bestIdx = pi; }
    }
    out[i] = palette[bestIdx][0];
    out[i + 1] = palette[bestIdx][1];
    out[i + 2] = palette[bestIdx][2];
  }
  return out;
}

// 對齊指定色板
function quantizeToPalette(data, palette) {
  if (!palette || palette.length === 0) return data;
  // 轉換 hex 為 RGB
  const rgbPalette = palette.map(hex => [
    parseInt(hex.slice(1, 3), 16),
    parseInt(hex.slice(3, 5), 16),
    parseInt(hex.slice(5, 7), 16),
  ]);
  const out = new Uint8ClampedArray(data);
  for (let i = 0; i < data.length; i += 4) {
    if (data[i + 3] < 128) continue;
    const r = data[i], g = data[i + 1], b = data[i + 2];
    let bestIdx = 0, bestDist = Infinity;
    for (let pi = 0; pi < rgbPalette.length; pi++) {
      const dr = r - rgbPalette[pi][0];
      const dg = g - rgbPalette[pi][1];
      const db = b - rgbPalette[pi][2];
      const d = dr * dr + dg * dg + db * db;
      if (d < bestDist) { bestDist = d; bestIdx = pi; }
    }
    out[i] = rgbPalette[bestIdx][0];
    out[i + 1] = rgbPalette[bestIdx][1];
    out[i + 2] = rgbPalette[bestIdx][2];
  }
  return out;
}

// 有序抖動（Bayer 4x4）
function applyOrderedDither(data, w, h, colorCount, strength) {
  const bayer4 = [
    [0, 8, 2, 10],
    [12, 4, 14, 6],
    [3, 11, 1, 9],
    [15, 7, 13, 5],
  ];
  const levels = Math.ceil(Math.cbrt(colorCount));
  const step = 255 / (levels - 1);
  const out = new Uint8ClampedArray(data);
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      const i = (y * w + x) * 4;
      if (data[i + 3] < 128) continue;
      const threshold = bayer4[y % 4][x % 4] / 16 - 0.5;
      for (let c = 0; c < 3; c++) {
        const orig = data[i + c];
        const quantized = Math.round(orig / step) * step;
        const error = (orig - quantized) * strength;
        out[i + c] = Math.max(0, Math.min(255, orig + error * 1.5));
      }
    }
  }
  return out;
}

// 像素外框（邊緣偵測，1px 深色邊）
function addPixelOutline(data, w, h) {
  const out = new Uint8ClampedArray(data);
  // 找出背景透明區域與不透明區域的邊界
  for (let y = 0; y < h; y++) {
    for (let x = 0; x < w; x++) {
      const i = (y * w + x) * 4;
      if (data[i + 3] >= 128) continue; // 已經有顏色
      // 檢查鄰居是否有不透明像素
      let hasOpaqueNeighbor = false;
      let minR = 255, minG = 255, minB = 255;
      for (let dy = -1; dy <= 1; dy++) {
        for (let dx = -1; dx <= 1; dx++) {
          if (dx === 0 && dy === 0) continue;
          const nx = x + dx, ny = y + dy;
          if (nx < 0 || nx >= w || ny < 0 || ny >= h) continue;
          const ni = (ny * w + nx) * 4;
          if (data[ni + 3] >= 128) {
            hasOpaqueNeighbor = true;
            if (data[ni] < minR) minR = data[ni];
            if (data[ni + 1] < minG) minG = data[ni + 1];
            if (data[ni + 2] < minB) minB = data[ni + 2];
          }
        }
      }
      if (hasOpaqueNeighbor) {
        // 以較深的顏色作為外框
        out[i] = Math.max(0, Math.floor(minR * 0.5));
        out[i + 1] = Math.max(0, Math.floor(minG * 0.5));
        out[i + 2] = Math.max(0, Math.floor(minB * 0.5));
        out[i + 3] = 255;
      }
    }
  }
  return out;
}

window.PixelView = PixelView;
window.PixelConvertDialog = PixelConvertDialog;
