@almadar/ui 5.57.0 → 5.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29895,13 +29895,13 @@ var init_MapView = __esm({
29895
29895
  shadowSize: [41, 41]
29896
29896
  });
29897
29897
  L.Marker.prototype.options.icon = defaultIcon;
29898
- const { useEffect: useEffect80, useRef: useRef78, useCallback: useCallback120, useState: useState110 } = React76__default;
29898
+ const { useEffect: useEffect81, useRef: useRef78, useCallback: useCallback120, useState: useState110 } = React76__default;
29899
29899
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29900
29900
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29901
29901
  function MapUpdater({ centerLat, centerLng, zoom }) {
29902
29902
  const map = useMap();
29903
29903
  const prevRef = useRef78({ centerLat, centerLng, zoom });
29904
- useEffect80(() => {
29904
+ useEffect81(() => {
29905
29905
  const prev = prevRef.current;
29906
29906
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29907
29907
  map.setView([centerLat, centerLng], zoom);
@@ -29912,7 +29912,7 @@ var init_MapView = __esm({
29912
29912
  }
29913
29913
  function MapClickHandler({ onMapClick }) {
29914
29914
  const map = useMap();
29915
- useEffect80(() => {
29915
+ useEffect81(() => {
29916
29916
  if (!onMapClick) return;
29917
29917
  const handler = (e) => {
29918
29918
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -38373,6 +38373,9 @@ var init_GraphCanvas = __esm({
38373
38373
  actions,
38374
38374
  onNodeClick,
38375
38375
  nodeClickEvent,
38376
+ selectedNodeId,
38377
+ repulsion = 800,
38378
+ linkDistance = 100,
38376
38379
  layout = "force",
38377
38380
  entity,
38378
38381
  isLoading = false,
@@ -38388,6 +38391,35 @@ var init_GraphCanvas = __esm({
38388
38391
  const [hoveredNode, setHoveredNode] = useState(null);
38389
38392
  const nodesRef = useRef([]);
38390
38393
  const [, forceUpdate] = useState(0);
38394
+ const interactionRef = useRef({
38395
+ mode: "none",
38396
+ dragNodeId: null,
38397
+ startMouse: { x: 0, y: 0 },
38398
+ startOffset: { x: 0, y: 0 },
38399
+ downPos: { x: 0, y: 0 }
38400
+ });
38401
+ const toCoords = useCallback(
38402
+ (e) => {
38403
+ const canvas = canvasRef.current;
38404
+ if (!canvas) return null;
38405
+ const rect = canvas.getBoundingClientRect();
38406
+ const screenX = e.clientX - rect.left;
38407
+ const screenY = e.clientY - rect.top;
38408
+ return {
38409
+ screenX,
38410
+ screenY,
38411
+ graphX: (screenX - offset.x) / zoom,
38412
+ graphY: (screenY - offset.y) / zoom
38413
+ };
38414
+ },
38415
+ [offset, zoom]
38416
+ );
38417
+ const nodeAt = useCallback((graphX, graphY) => {
38418
+ return nodesRef.current.find((n) => {
38419
+ const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
38420
+ return dist < (n.size || 8) + 4;
38421
+ });
38422
+ }, []);
38391
38423
  const handleAction = useCallback(
38392
38424
  (action) => {
38393
38425
  if (action.event) {
@@ -38453,7 +38485,7 @@ var init_GraphCanvas = __esm({
38453
38485
  const dx = nodes[j].x - nodes[i].x;
38454
38486
  const dy = nodes[j].y - nodes[i].y;
38455
38487
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38456
- const force = 800 / (dist * dist);
38488
+ const force = repulsion / (dist * dist);
38457
38489
  const fx = dx / dist * force;
38458
38490
  const fy = dy / dist * force;
38459
38491
  nodes[i].fx -= fx;
@@ -38469,7 +38501,7 @@ var init_GraphCanvas = __esm({
38469
38501
  const dx = target.x - source.x;
38470
38502
  const dy = target.y - source.y;
38471
38503
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38472
- const force = (dist - 100) * 0.05;
38504
+ const force = (dist - linkDistance) * 0.05;
38473
38505
  const fx = dx / dist * force;
38474
38506
  const fy = dy / dist * force;
38475
38507
  source.fx += fx;
@@ -38503,7 +38535,7 @@ var init_GraphCanvas = __esm({
38503
38535
  return () => {
38504
38536
  cancelAnimationFrame(animRef.current);
38505
38537
  };
38506
- }, [propNodes, propEdges, layout]);
38538
+ }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38507
38539
  useEffect(() => {
38508
38540
  const canvas = canvasRef.current;
38509
38541
  if (!canvas) return;
@@ -38539,18 +38571,25 @@ var init_GraphCanvas = __esm({
38539
38571
  const size = node.size || 8;
38540
38572
  const color = node.color || getGroupColor(node.group, groups);
38541
38573
  const isHovered = hoveredNode === node.id;
38574
+ const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38575
+ const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38542
38576
  ctx.beginPath();
38543
- ctx.arc(node.x, node.y, isHovered ? size + 2 : size, 0, Math.PI * 2);
38577
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
38544
38578
  ctx.fillStyle = color;
38545
38579
  ctx.fill();
38546
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
38547
- ctx.lineWidth = isHovered ? 2 : 1;
38580
+ if (isSelected) {
38581
+ ctx.strokeStyle = "var(--color-accent)";
38582
+ ctx.lineWidth = 3;
38583
+ } else {
38584
+ ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
38585
+ ctx.lineWidth = isHovered ? 2 : 1;
38586
+ }
38548
38587
  ctx.stroke();
38549
38588
  if (showLabels && node.label) {
38550
38589
  ctx.fillStyle = "#666666";
38551
- ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
38590
+ ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
38552
38591
  ctx.textAlign = "center";
38553
- ctx.fillText(node.label, node.x, node.y + size + 12);
38592
+ ctx.fillText(node.label, node.x, node.y + radius + 12);
38554
38593
  }
38555
38594
  }
38556
38595
  ctx.restore();
@@ -38561,6 +38600,97 @@ var init_GraphCanvas = __esm({
38561
38600
  setZoom(1);
38562
38601
  setOffset({ x: 0, y: 0 });
38563
38602
  }, []);
38603
+ const handleWheel = useCallback(
38604
+ (e) => {
38605
+ if (!interactive) return;
38606
+ e.preventDefault();
38607
+ const coords = toCoords(e);
38608
+ if (!coords) return;
38609
+ const oldZoom = zoom;
38610
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
38611
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38612
+ if (newZoom === oldZoom) return;
38613
+ setOffset((o) => ({
38614
+ x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
38615
+ y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
38616
+ }));
38617
+ setZoom(newZoom);
38618
+ },
38619
+ [interactive, toCoords, zoom]
38620
+ );
38621
+ const handleMouseDown = useCallback(
38622
+ (e) => {
38623
+ const coords = toCoords(e);
38624
+ if (!coords) return;
38625
+ const node = nodeAt(coords.graphX, coords.graphY);
38626
+ const state = interactionRef.current;
38627
+ state.downPos = { x: e.clientX, y: e.clientY };
38628
+ state.startMouse = { x: e.clientX, y: e.clientY };
38629
+ state.startOffset = { ...offset };
38630
+ if (draggable && node) {
38631
+ state.mode = "dragging";
38632
+ state.dragNodeId = node.id;
38633
+ } else if (interactive) {
38634
+ state.mode = "panning";
38635
+ state.dragNodeId = null;
38636
+ } else {
38637
+ state.mode = "none";
38638
+ state.dragNodeId = null;
38639
+ }
38640
+ },
38641
+ [toCoords, nodeAt, draggable, interactive, offset]
38642
+ );
38643
+ const handleMouseMove = useCallback(
38644
+ (e) => {
38645
+ const state = interactionRef.current;
38646
+ if (state.mode === "panning") {
38647
+ const dx = e.clientX - state.startMouse.x;
38648
+ const dy = e.clientY - state.startMouse.y;
38649
+ setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
38650
+ return;
38651
+ }
38652
+ if (state.mode === "dragging" && state.dragNodeId) {
38653
+ const coords2 = toCoords(e);
38654
+ if (!coords2) return;
38655
+ const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
38656
+ if (node2) {
38657
+ node2.x = coords2.graphX;
38658
+ node2.y = coords2.graphY;
38659
+ node2.vx = 0;
38660
+ node2.vy = 0;
38661
+ forceUpdate((n) => n + 1);
38662
+ }
38663
+ return;
38664
+ }
38665
+ const coords = toCoords(e);
38666
+ if (!coords) return;
38667
+ const node = nodeAt(coords.graphX, coords.graphY);
38668
+ setHoveredNode(node?.id ?? null);
38669
+ },
38670
+ [toCoords, nodeAt]
38671
+ );
38672
+ const handleMouseUp = useCallback(
38673
+ (e) => {
38674
+ const state = interactionRef.current;
38675
+ const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
38676
+ state.mode = "none";
38677
+ state.dragNodeId = null;
38678
+ if (moved < 4) {
38679
+ const coords = toCoords(e);
38680
+ if (!coords) return;
38681
+ const node = nodeAt(coords.graphX, coords.graphY);
38682
+ if (node) {
38683
+ handleNodeClick(node);
38684
+ }
38685
+ }
38686
+ },
38687
+ [toCoords, nodeAt, handleNodeClick]
38688
+ );
38689
+ const handleMouseLeave = useCallback(() => {
38690
+ interactionRef.current.mode = "none";
38691
+ interactionRef.current.dragNodeId = null;
38692
+ setHoveredNode(null);
38693
+ }, []);
38564
38694
  if (isLoading) {
38565
38695
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
38566
38696
  }
@@ -38622,20 +38752,11 @@ var init_GraphCanvas = __esm({
38622
38752
  height,
38623
38753
  className: "w-full cursor-grab active:cursor-grabbing",
38624
38754
  style: { height },
38625
- onClick: (e) => {
38626
- const canvas = canvasRef.current;
38627
- if (!canvas) return;
38628
- const rect = canvas.getBoundingClientRect();
38629
- const x = (e.clientX - rect.left - offset.x) / zoom;
38630
- const y = (e.clientY - rect.top - offset.y) / zoom;
38631
- const clickedNode = nodesRef.current.find((n) => {
38632
- const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
38633
- return dist < (n.size || 8) + 4;
38634
- });
38635
- if (clickedNode) {
38636
- handleNodeClick(clickedNode);
38637
- }
38638
- }
38755
+ onWheel: handleWheel,
38756
+ onMouseDown: handleMouseDown,
38757
+ onMouseMove: handleMouseMove,
38758
+ onMouseUp: handleMouseUp,
38759
+ onMouseLeave: handleMouseLeave
38639
38760
  }
38640
38761
  ) }),
38641
38762
  groups.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
@@ -50454,12 +50575,25 @@ function pathToFeatures(path) {
50454
50575
  sprite: `${CDN6}world-map/road_straight.png`
50455
50576
  }));
50456
50577
  }
50578
+ function heroToUnit(hero) {
50579
+ return {
50580
+ id: hero.id,
50581
+ position: { x: hero.x, y: hero.y },
50582
+ name: "Hero",
50583
+ team: "player",
50584
+ sprite: HERO_SPRITE,
50585
+ unitType: "amir",
50586
+ health: 1,
50587
+ maxHealth: 1
50588
+ };
50589
+ }
50457
50590
  function TowerDefenseBoard({
50458
50591
  entity,
50459
50592
  tiles: propTiles,
50460
50593
  path: propPath,
50461
50594
  towers: propTowers,
50462
50595
  creeps: propCreeps,
50596
+ hero: propHero,
50463
50597
  gold: propGold,
50464
50598
  lives: propLives,
50465
50599
  wave: propWave,
@@ -50475,6 +50609,7 @@ function TowerDefenseBoard({
50475
50609
  startWaveEvent,
50476
50610
  playAgainEvent,
50477
50611
  gameEndEvent,
50612
+ moveHeroEvent,
50478
50613
  className
50479
50614
  }) {
50480
50615
  const board = boardEntity(entity) ?? {};
@@ -50486,6 +50621,7 @@ function TowerDefenseBoard({
50486
50621
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
50487
50622
  const towers = propTowers ?? rows(board.towers);
50488
50623
  const creeps = propCreeps ?? rows(board.creeps);
50624
+ const hero = propHero ?? { id: "hero", x: 8, y: 8 };
50489
50625
  const gold = propGold ?? num(board.gold, 100);
50490
50626
  const lives = propLives ?? num(board.lives, 20);
50491
50627
  const wave = propWave ?? num(board.wave, 1);
@@ -50503,6 +50639,31 @@ function TowerDefenseBoard({
50503
50639
  if (result === "none") {
50504
50640
  emittedGameEnd.current = false;
50505
50641
  }
50642
+ const moveHeroEventRef = useRef(moveHeroEvent);
50643
+ moveHeroEventRef.current = moveHeroEvent;
50644
+ const resultRef = useRef(result);
50645
+ resultRef.current = result;
50646
+ useEffect(() => {
50647
+ function onKeyDown(e) {
50648
+ const evt = moveHeroEventRef.current;
50649
+ if (!evt || resultRef.current !== "none") return;
50650
+ let dx = 0;
50651
+ let dy = 0;
50652
+ if (e.key === "ArrowUp") {
50653
+ dy = -1;
50654
+ } else if (e.key === "ArrowDown") {
50655
+ dy = 1;
50656
+ } else if (e.key === "ArrowLeft") {
50657
+ dx = -1;
50658
+ } else if (e.key === "ArrowRight") {
50659
+ dx = 1;
50660
+ } else return;
50661
+ e.preventDefault();
50662
+ eventBus.emit(`UI:${evt}`, { dx, dy });
50663
+ }
50664
+ window.addEventListener("keydown", onKeyDown);
50665
+ return () => window.removeEventListener("keydown", onKeyDown);
50666
+ }, [eventBus]);
50506
50667
  const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
50507
50668
  const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
50508
50669
  const validMoves = useMemo(() => {
@@ -50514,7 +50675,8 @@ function TowerDefenseBoard({
50514
50675
  const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
50515
50676
  const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
50516
50677
  const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
50517
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
50678
+ const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
50679
+ const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
50518
50680
  const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
50519
50681
  const handleTileClick = useCallback((x, y) => {
50520
50682
  if (result !== "none") return;
@@ -50613,7 +50775,7 @@ function TowerDefenseBoard({
50613
50775
  ] }) })
50614
50776
  ] });
50615
50777
  }
50616
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
50778
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
50617
50779
  var init_TowerDefenseBoard = __esm({
50618
50780
  "components/game/organisms/TowerDefenseBoard.tsx"() {
50619
50781
  "use client";
@@ -50687,8 +50849,9 @@ var init_TowerDefenseBoard = __esm({
50687
50849
  { x: 13, y: 15 }
50688
50850
  ];
50689
50851
  DEFAULT_TD_TILES = buildDefaultTDTiles();
50690
- TOWER_SPRITE = `${CDN6}units/guardian.png`;
50691
- CREEP_SPRITE = `${CDN6}units/scrapper.png`;
50852
+ TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
50853
+ CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
50854
+ HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
50692
50855
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
50693
50856
  }
50694
50857
  });