@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.
@@ -29475,13 +29475,13 @@ var init_MapView = __esm({
29475
29475
  shadowSize: [41, 41]
29476
29476
  });
29477
29477
  L.Marker.prototype.options.icon = defaultIcon;
29478
- const { useEffect: useEffect79, useRef: useRef75, useCallback: useCallback118, useState: useState109 } = React82__default;
29478
+ const { useEffect: useEffect80, useRef: useRef75, useCallback: useCallback118, useState: useState109 } = React82__default;
29479
29479
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29480
29480
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29481
29481
  function MapUpdater({ centerLat, centerLng, zoom }) {
29482
29482
  const map = useMap();
29483
29483
  const prevRef = useRef75({ centerLat, centerLng, zoom });
29484
- useEffect79(() => {
29484
+ useEffect80(() => {
29485
29485
  const prev = prevRef.current;
29486
29486
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29487
29487
  map.setView([centerLat, centerLng], zoom);
@@ -29492,7 +29492,7 @@ var init_MapView = __esm({
29492
29492
  }
29493
29493
  function MapClickHandler({ onMapClick }) {
29494
29494
  const map = useMap();
29495
- useEffect79(() => {
29495
+ useEffect80(() => {
29496
29496
  if (!onMapClick) return;
29497
29497
  const handler = (e) => {
29498
29498
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -37953,6 +37953,9 @@ var init_GraphCanvas = __esm({
37953
37953
  actions,
37954
37954
  onNodeClick,
37955
37955
  nodeClickEvent,
37956
+ selectedNodeId,
37957
+ repulsion = 800,
37958
+ linkDistance = 100,
37956
37959
  layout = "force",
37957
37960
  entity,
37958
37961
  isLoading = false,
@@ -37968,6 +37971,35 @@ var init_GraphCanvas = __esm({
37968
37971
  const [hoveredNode, setHoveredNode] = useState(null);
37969
37972
  const nodesRef = useRef([]);
37970
37973
  const [, forceUpdate] = useState(0);
37974
+ const interactionRef = useRef({
37975
+ mode: "none",
37976
+ dragNodeId: null,
37977
+ startMouse: { x: 0, y: 0 },
37978
+ startOffset: { x: 0, y: 0 },
37979
+ downPos: { x: 0, y: 0 }
37980
+ });
37981
+ const toCoords = useCallback(
37982
+ (e) => {
37983
+ const canvas = canvasRef.current;
37984
+ if (!canvas) return null;
37985
+ const rect = canvas.getBoundingClientRect();
37986
+ const screenX = e.clientX - rect.left;
37987
+ const screenY = e.clientY - rect.top;
37988
+ return {
37989
+ screenX,
37990
+ screenY,
37991
+ graphX: (screenX - offset.x) / zoom,
37992
+ graphY: (screenY - offset.y) / zoom
37993
+ };
37994
+ },
37995
+ [offset, zoom]
37996
+ );
37997
+ const nodeAt = useCallback((graphX, graphY) => {
37998
+ return nodesRef.current.find((n) => {
37999
+ const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
38000
+ return dist < (n.size || 8) + 4;
38001
+ });
38002
+ }, []);
37971
38003
  const handleAction = useCallback(
37972
38004
  (action) => {
37973
38005
  if (action.event) {
@@ -38033,7 +38065,7 @@ var init_GraphCanvas = __esm({
38033
38065
  const dx = nodes[j].x - nodes[i].x;
38034
38066
  const dy = nodes[j].y - nodes[i].y;
38035
38067
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38036
- const force = 800 / (dist * dist);
38068
+ const force = repulsion / (dist * dist);
38037
38069
  const fx = dx / dist * force;
38038
38070
  const fy = dy / dist * force;
38039
38071
  nodes[i].fx -= fx;
@@ -38049,7 +38081,7 @@ var init_GraphCanvas = __esm({
38049
38081
  const dx = target.x - source.x;
38050
38082
  const dy = target.y - source.y;
38051
38083
  const dist = Math.sqrt(dx * dx + dy * dy) || 1;
38052
- const force = (dist - 100) * 0.05;
38084
+ const force = (dist - linkDistance) * 0.05;
38053
38085
  const fx = dx / dist * force;
38054
38086
  const fy = dy / dist * force;
38055
38087
  source.fx += fx;
@@ -38083,7 +38115,7 @@ var init_GraphCanvas = __esm({
38083
38115
  return () => {
38084
38116
  cancelAnimationFrame(animRef.current);
38085
38117
  };
38086
- }, [propNodes, propEdges, layout]);
38118
+ }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38087
38119
  useEffect(() => {
38088
38120
  const canvas = canvasRef.current;
38089
38121
  if (!canvas) return;
@@ -38119,18 +38151,25 @@ var init_GraphCanvas = __esm({
38119
38151
  const size = node.size || 8;
38120
38152
  const color = node.color || getGroupColor(node.group, groups);
38121
38153
  const isHovered = hoveredNode === node.id;
38154
+ const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38155
+ const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38122
38156
  ctx.beginPath();
38123
- ctx.arc(node.x, node.y, isHovered ? size + 2 : size, 0, Math.PI * 2);
38157
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
38124
38158
  ctx.fillStyle = color;
38125
38159
  ctx.fill();
38126
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
38127
- ctx.lineWidth = isHovered ? 2 : 1;
38160
+ if (isSelected) {
38161
+ ctx.strokeStyle = "var(--color-accent)";
38162
+ ctx.lineWidth = 3;
38163
+ } else {
38164
+ ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
38165
+ ctx.lineWidth = isHovered ? 2 : 1;
38166
+ }
38128
38167
  ctx.stroke();
38129
38168
  if (showLabels && node.label) {
38130
38169
  ctx.fillStyle = "#666666";
38131
- ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
38170
+ ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
38132
38171
  ctx.textAlign = "center";
38133
- ctx.fillText(node.label, node.x, node.y + size + 12);
38172
+ ctx.fillText(node.label, node.x, node.y + radius + 12);
38134
38173
  }
38135
38174
  }
38136
38175
  ctx.restore();
@@ -38141,6 +38180,97 @@ var init_GraphCanvas = __esm({
38141
38180
  setZoom(1);
38142
38181
  setOffset({ x: 0, y: 0 });
38143
38182
  }, []);
38183
+ const handleWheel = useCallback(
38184
+ (e) => {
38185
+ if (!interactive) return;
38186
+ e.preventDefault();
38187
+ const coords = toCoords(e);
38188
+ if (!coords) return;
38189
+ const oldZoom = zoom;
38190
+ const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
38191
+ const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
38192
+ if (newZoom === oldZoom) return;
38193
+ setOffset((o) => ({
38194
+ x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
38195
+ y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
38196
+ }));
38197
+ setZoom(newZoom);
38198
+ },
38199
+ [interactive, toCoords, zoom]
38200
+ );
38201
+ const handleMouseDown = useCallback(
38202
+ (e) => {
38203
+ const coords = toCoords(e);
38204
+ if (!coords) return;
38205
+ const node = nodeAt(coords.graphX, coords.graphY);
38206
+ const state = interactionRef.current;
38207
+ state.downPos = { x: e.clientX, y: e.clientY };
38208
+ state.startMouse = { x: e.clientX, y: e.clientY };
38209
+ state.startOffset = { ...offset };
38210
+ if (draggable && node) {
38211
+ state.mode = "dragging";
38212
+ state.dragNodeId = node.id;
38213
+ } else if (interactive) {
38214
+ state.mode = "panning";
38215
+ state.dragNodeId = null;
38216
+ } else {
38217
+ state.mode = "none";
38218
+ state.dragNodeId = null;
38219
+ }
38220
+ },
38221
+ [toCoords, nodeAt, draggable, interactive, offset]
38222
+ );
38223
+ const handleMouseMove = useCallback(
38224
+ (e) => {
38225
+ const state = interactionRef.current;
38226
+ if (state.mode === "panning") {
38227
+ const dx = e.clientX - state.startMouse.x;
38228
+ const dy = e.clientY - state.startMouse.y;
38229
+ setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
38230
+ return;
38231
+ }
38232
+ if (state.mode === "dragging" && state.dragNodeId) {
38233
+ const coords2 = toCoords(e);
38234
+ if (!coords2) return;
38235
+ const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
38236
+ if (node2) {
38237
+ node2.x = coords2.graphX;
38238
+ node2.y = coords2.graphY;
38239
+ node2.vx = 0;
38240
+ node2.vy = 0;
38241
+ forceUpdate((n) => n + 1);
38242
+ }
38243
+ return;
38244
+ }
38245
+ const coords = toCoords(e);
38246
+ if (!coords) return;
38247
+ const node = nodeAt(coords.graphX, coords.graphY);
38248
+ setHoveredNode(node?.id ?? null);
38249
+ },
38250
+ [toCoords, nodeAt]
38251
+ );
38252
+ const handleMouseUp = useCallback(
38253
+ (e) => {
38254
+ const state = interactionRef.current;
38255
+ const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
38256
+ state.mode = "none";
38257
+ state.dragNodeId = null;
38258
+ if (moved < 4) {
38259
+ const coords = toCoords(e);
38260
+ if (!coords) return;
38261
+ const node = nodeAt(coords.graphX, coords.graphY);
38262
+ if (node) {
38263
+ handleNodeClick(node);
38264
+ }
38265
+ }
38266
+ },
38267
+ [toCoords, nodeAt, handleNodeClick]
38268
+ );
38269
+ const handleMouseLeave = useCallback(() => {
38270
+ interactionRef.current.mode = "none";
38271
+ interactionRef.current.dragNodeId = null;
38272
+ setHoveredNode(null);
38273
+ }, []);
38144
38274
  if (isLoading) {
38145
38275
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
38146
38276
  }
@@ -38202,20 +38332,11 @@ var init_GraphCanvas = __esm({
38202
38332
  height,
38203
38333
  className: "w-full cursor-grab active:cursor-grabbing",
38204
38334
  style: { height },
38205
- onClick: (e) => {
38206
- const canvas = canvasRef.current;
38207
- if (!canvas) return;
38208
- const rect = canvas.getBoundingClientRect();
38209
- const x = (e.clientX - rect.left - offset.x) / zoom;
38210
- const y = (e.clientY - rect.top - offset.y) / zoom;
38211
- const clickedNode = nodesRef.current.find((n) => {
38212
- const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
38213
- return dist < (n.size || 8) + 4;
38214
- });
38215
- if (clickedNode) {
38216
- handleNodeClick(clickedNode);
38217
- }
38218
- }
38335
+ onWheel: handleWheel,
38336
+ onMouseDown: handleMouseDown,
38337
+ onMouseMove: handleMouseMove,
38338
+ onMouseUp: handleMouseUp,
38339
+ onMouseLeave: handleMouseLeave
38219
38340
  }
38220
38341
  ) }),
38221
38342
  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: [
@@ -49292,12 +49413,25 @@ function pathToFeatures(path) {
49292
49413
  sprite: `${CDN6}world-map/road_straight.png`
49293
49414
  }));
49294
49415
  }
49416
+ function heroToUnit(hero) {
49417
+ return {
49418
+ id: hero.id,
49419
+ position: { x: hero.x, y: hero.y },
49420
+ name: "Hero",
49421
+ team: "player",
49422
+ sprite: HERO_SPRITE,
49423
+ unitType: "amir",
49424
+ health: 1,
49425
+ maxHealth: 1
49426
+ };
49427
+ }
49295
49428
  function TowerDefenseBoard({
49296
49429
  entity,
49297
49430
  tiles: propTiles,
49298
49431
  path: propPath,
49299
49432
  towers: propTowers,
49300
49433
  creeps: propCreeps,
49434
+ hero: propHero,
49301
49435
  gold: propGold,
49302
49436
  lives: propLives,
49303
49437
  wave: propWave,
@@ -49313,6 +49447,7 @@ function TowerDefenseBoard({
49313
49447
  startWaveEvent,
49314
49448
  playAgainEvent,
49315
49449
  gameEndEvent,
49450
+ moveHeroEvent,
49316
49451
  className
49317
49452
  }) {
49318
49453
  const board = boardEntity(entity) ?? {};
@@ -49324,6 +49459,7 @@ function TowerDefenseBoard({
49324
49459
  const path = rawPath.length > 0 ? rawPath : DEFAULT_TD_PATH;
49325
49460
  const towers = propTowers ?? rows(board.towers);
49326
49461
  const creeps = propCreeps ?? rows(board.creeps);
49462
+ const hero = propHero ?? { id: "hero", x: 8, y: 8 };
49327
49463
  const gold = propGold ?? num(board.gold, 100);
49328
49464
  const lives = propLives ?? num(board.lives, 20);
49329
49465
  const wave = propWave ?? num(board.wave, 1);
@@ -49341,6 +49477,31 @@ function TowerDefenseBoard({
49341
49477
  if (result === "none") {
49342
49478
  emittedGameEnd.current = false;
49343
49479
  }
49480
+ const moveHeroEventRef = useRef(moveHeroEvent);
49481
+ moveHeroEventRef.current = moveHeroEvent;
49482
+ const resultRef = useRef(result);
49483
+ resultRef.current = result;
49484
+ useEffect(() => {
49485
+ function onKeyDown(e) {
49486
+ const evt = moveHeroEventRef.current;
49487
+ if (!evt || resultRef.current !== "none") return;
49488
+ let dx = 0;
49489
+ let dy = 0;
49490
+ if (e.key === "ArrowUp") {
49491
+ dy = -1;
49492
+ } else if (e.key === "ArrowDown") {
49493
+ dy = 1;
49494
+ } else if (e.key === "ArrowLeft") {
49495
+ dx = -1;
49496
+ } else if (e.key === "ArrowRight") {
49497
+ dx = 1;
49498
+ } else return;
49499
+ e.preventDefault();
49500
+ eventBus.emit(`UI:${evt}`, { dx, dy });
49501
+ }
49502
+ window.addEventListener("keydown", onKeyDown);
49503
+ return () => window.removeEventListener("keydown", onKeyDown);
49504
+ }, [eventBus]);
49344
49505
  const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
49345
49506
  const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
49346
49507
  const validMoves = useMemo(() => {
@@ -49352,7 +49513,8 @@ function TowerDefenseBoard({
49352
49513
  const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
49353
49514
  const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
49354
49515
  const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
49355
- const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
49516
+ const heroUnit = useMemo(() => heroToUnit(hero), [hero]);
49517
+ const isoUnits = useMemo(() => [...towerUnits, ...creepUnits, heroUnit], [towerUnits, creepUnits, heroUnit]);
49356
49518
  const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
49357
49519
  const handleTileClick = useCallback((x, y) => {
49358
49520
  if (result !== "none") return;
@@ -49451,7 +49613,7 @@ function TowerDefenseBoard({
49451
49613
  ] }) })
49452
49614
  ] });
49453
49615
  }
49454
- var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE;
49616
+ var CDN6, TD_GRID_W, TD_GRID_H, TERRAIN_SPRITES, DEFAULT_TD_PATH, DEFAULT_TD_TILES, TOWER_SPRITE, CREEP_SPRITE, HERO_SPRITE;
49455
49617
  var init_TowerDefenseBoard = __esm({
49456
49618
  "components/game/organisms/TowerDefenseBoard.tsx"() {
49457
49619
  "use client";
@@ -49525,8 +49687,9 @@ var init_TowerDefenseBoard = __esm({
49525
49687
  { x: 13, y: 15 }
49526
49688
  ];
49527
49689
  DEFAULT_TD_TILES = buildDefaultTDTiles();
49528
- TOWER_SPRITE = `${CDN6}units/guardian.png`;
49529
- CREEP_SPRITE = `${CDN6}units/scrapper.png`;
49690
+ TOWER_SPRITE = `${CDN6}sprite-sheets/guardian-sprite-sheet-se.png`;
49691
+ CREEP_SPRITE = `${CDN6}sprite-sheets/scrapper-sprite-sheet-se.png`;
49692
+ HERO_SPRITE = `${CDN6}sprite-sheets/amir-sprite-sheet-se.png`;
49530
49693
  TowerDefenseBoard.displayName = "TowerDefenseBoard";
49531
49694
  }
49532
49695
  });