File size: 3,901 Bytes
9d34bb7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { useState } from 'react';
import { Maximize2, Minimize2, Navigation } from 'lucide-react';
import { useAppStore } from '../store';

export const Minimap = () => {
  const { images, pan, zoom, setPan, isBrowserOpen, isLibraryOpen, isSettingsOpen } = useAppStore();
  const [isCollapsed, setIsCollapsed] = useState(false);
  if (images.length === 0) return null;

  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
  images.forEach(img => {
    minX = Math.min(minX, img.x);
    minY = Math.min(minY, img.y);
    maxX = Math.max(maxX, img.x + img.width);
    maxY = Math.max(maxY, img.y + img.height);
  });

  const contentW = Math.max(1, maxX - minX);
  const contentH = Math.max(1, maxY - minY);
  const pad = Math.max(200, Math.max(contentW, contentH) * 0.15);
  minX -= pad; minY -= pad; maxX += pad; maxY += pad;
  const width = Math.max(1, maxX - minX);
  const height = Math.max(1, maxY - minY);

  const MAP_W = 160;
  const MAP_H = 120;
  const scale = Math.min(MAP_W / width, MAP_H / height);
  const ox = (MAP_W - width * scale) / 2;
  const oy = (MAP_H - height * scale) / 2;

  const vpX = -pan.x / zoom;
  const vpY = -pan.y / zoom;
  const vpW = window.innerWidth / zoom;
  const vpH = window.innerHeight / zoom;

  const safeRight = isSettingsOpen ? 24 : isBrowserOpen ? 24 : 20;
  const safeLeftPanel = isLibraryOpen ? 8 : 20;
  const wrapperStyle: React.CSSProperties = {
    position: 'fixed',
    right: safeRight,
    bottom: 20,
    zIndex: 45,
    maxWidth: 'calc(100vw - 40px)',
    maxHeight: 'calc(100vh - 40px)',
  };

  const handleClick = (e: React.PointerEvent<HTMLDivElement>) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;
    const worldX = (mx - ox) / scale + minX;
    const worldY = (my - oy) / scale + minY;
    setPan({ x: -worldX * zoom + window.innerWidth / 2, y: -worldY * zoom + window.innerHeight / 2 });
  };

  if (isCollapsed) {
    return (
      <div style={wrapperStyle}>
        <button className="w-9 h-9 bg-[#1C1C1E]/90 border border-[#3A3A3E] rounded-xl shadow-2xl backdrop-blur flex items-center justify-center text-[#A0A0A0] hover:text-white hover:border-[#0A84FF]/60 transition-colors" onClick={() => setIsCollapsed(false)} title="Show navigator">
          <Navigation size={15} />
        </button>
      </div>
    );
  }

  return (
    <div style={wrapperStyle} className="w-[160px] bg-[#1C1C1E]/92 border border-[#3A3A3E] rounded-xl shadow-2xl overflow-hidden backdrop-blur-md opacity-70 hover:opacity-100 transition-opacity pointer-events-auto">
      <div className="h-7 px-2.5 flex items-center justify-between border-b border-[#3A3A3E]/70 bg-black/20">
        <div className="flex items-center gap-1.5 text-[10px] font-semibold text-[#A0A0A0] uppercase tracking-wider">
          <Navigation size={11} /> Navigator
        </div>
        <button className="w-5 h-5 hover:bg-white/10 rounded flex items-center justify-center text-white/60 hover:text-white" onClick={e => { e.stopPropagation(); setIsCollapsed(true); }} title="Collapse navigator">
          <Minimize2 size={10} />
        </button>
      </div>
      <div className="relative cursor-crosshair bg-[#111113]" style={{ width: MAP_W, height: MAP_H }} onPointerDown={handleClick}>
        {images.map(img => (
          <div key={img.id} className="absolute bg-[#8A8A8C]/55 rounded-[1px]" style={{ left: ox + (img.x - minX) * scale, top: oy + (img.y - minY) * scale, width: Math.max(2, img.width * scale), height: Math.max(2, img.height * scale) }} />
        ))}
        <div className="absolute border border-[#0A84FF] bg-[#0A84FF]/15 pointer-events-none rounded-[2px]" style={{ left: ox + (vpX - minX) * scale, top: oy + (vpY - minY) * scale, width: Math.max(8, vpW * scale), height: Math.max(8, vpH * scale) }} />
      </div>
    </div>
  );
};