@almadar/ui 5.139.0 → 5.140.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.
@@ -12639,6 +12639,12 @@ function createWebPainter(ctx, onAssetLoad) {
12639
12639
  for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
12640
12640
  if (closed) ctx.closePath();
12641
12641
  };
12642
+ const toCanvasStyle = (style) => {
12643
+ if (typeof style === "string") return style;
12644
+ const g = style.kind === "linear" ? ctx.createLinearGradient(style.x1, style.y1, style.x2, style.y2) : ctx.createRadialGradient(style.cx, style.cy, 0, style.cx, style.cy, style.r);
12645
+ for (const stop of style.stops) g.addColorStop(stop.offset, stop.color);
12646
+ return g;
12647
+ };
12642
12648
  return {
12643
12649
  setViewport(width, height, dpr) {
12644
12650
  vw = width;
@@ -12692,47 +12698,47 @@ function createWebPainter(ctx, onAssetLoad) {
12692
12698
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
12693
12699
  }
12694
12700
  },
12695
- fillRect(x, y, w, h, color) {
12696
- ctx.fillStyle = color;
12701
+ fillRect(x, y, w, h, style) {
12702
+ ctx.fillStyle = toCanvasStyle(style);
12697
12703
  ctx.fillRect(x, y, w, h);
12698
12704
  },
12699
- strokeRect(x, y, w, h, color, lineWidth = 1) {
12700
- ctx.strokeStyle = color;
12705
+ strokeRect(x, y, w, h, style, lineWidth = 1) {
12706
+ ctx.strokeStyle = toCanvasStyle(style);
12701
12707
  ctx.lineWidth = lineWidth;
12702
12708
  ctx.strokeRect(x, y, w, h);
12703
12709
  },
12704
- fillPoly(points, color) {
12710
+ fillPoly(points, style) {
12705
12711
  if (points.length === 0) return;
12706
12712
  tracePoly(points, true);
12707
- ctx.fillStyle = color;
12713
+ ctx.fillStyle = toCanvasStyle(style);
12708
12714
  ctx.fill();
12709
12715
  },
12710
- strokePoly(points, color, lineWidth = 1, closed = false) {
12716
+ strokePoly(points, style, lineWidth = 1, closed = false) {
12711
12717
  if (points.length === 0) return;
12712
12718
  tracePoly(points, closed);
12713
- ctx.strokeStyle = color;
12719
+ ctx.strokeStyle = toCanvasStyle(style);
12714
12720
  ctx.lineWidth = lineWidth;
12715
12721
  ctx.stroke();
12716
12722
  },
12717
- fillEllipse(cx, cy, rx, ry, color) {
12723
+ fillEllipse(cx, cy, rx, ry, style) {
12718
12724
  ctx.beginPath();
12719
12725
  ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
12720
- ctx.fillStyle = color;
12726
+ ctx.fillStyle = toCanvasStyle(style);
12721
12727
  ctx.fill();
12722
12728
  },
12723
- strokeEllipse(cx, cy, rx, ry, color, lineWidth = 1) {
12729
+ strokeEllipse(cx, cy, rx, ry, style, lineWidth = 1) {
12724
12730
  ctx.beginPath();
12725
12731
  ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
12726
- ctx.strokeStyle = color;
12732
+ ctx.strokeStyle = toCanvasStyle(style);
12727
12733
  ctx.lineWidth = lineWidth;
12728
12734
  ctx.stroke();
12729
12735
  },
12730
- fillPath(d, color) {
12731
- ctx.fillStyle = color;
12736
+ fillPath(d, style) {
12737
+ ctx.fillStyle = toCanvasStyle(style);
12732
12738
  ctx.fill(new Path2D(d));
12733
12739
  },
12734
- strokePath(d, color, lineWidth = 1) {
12735
- ctx.strokeStyle = color;
12740
+ strokePath(d, style, lineWidth = 1) {
12741
+ ctx.strokeStyle = toCanvasStyle(style);
12736
12742
  ctx.lineWidth = lineWidth;
12737
12743
  ctx.stroke(new Path2D(d));
12738
12744
  },
@@ -12907,6 +12913,84 @@ var init_registry = __esm({
12907
12913
  DrawableRegistryContext = React93.createContext(null);
12908
12914
  }
12909
12915
  });
12916
+ function isAnimatedShape(node) {
12917
+ return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
12918
+ }
12919
+ function applyShapeAnimation(node, timeMs) {
12920
+ const anim = node.animation;
12921
+ if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
12922
+ const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
12923
+ const cycle = timeMs / anim.durationMs;
12924
+ const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
12925
+ const out = { ...node };
12926
+ const trackValue = (key) => {
12927
+ const defined = frames.filter((f3) => f3[key] !== void 0);
12928
+ if (defined.length === 0) return void 0;
12929
+ let prev;
12930
+ let next;
12931
+ for (const f3 of defined) {
12932
+ if (f3.at <= t) prev = f3;
12933
+ else if (!next) next = f3;
12934
+ }
12935
+ if (!prev) return defined[0][key];
12936
+ if (!next) return prev[key];
12937
+ const span = next.at - prev.at;
12938
+ const k = span > 0 ? (t - prev.at) / span : 1;
12939
+ const a = prev[key];
12940
+ const b = next[key];
12941
+ if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
12942
+ return a;
12943
+ };
12944
+ for (const key of NUMERIC_TRACKS) {
12945
+ const v = trackValue(key);
12946
+ if (v !== void 0) out[key] = v;
12947
+ }
12948
+ const fill = trackValue("fill");
12949
+ if (fill !== void 0) out.fill = fill;
12950
+ const stroke = trackValue("stroke");
12951
+ if (stroke !== void 0) out.stroke = stroke;
12952
+ const shadowFrames = frames.filter((f3) => f3.shadow !== void 0);
12953
+ if (shadowFrames.length > 0) {
12954
+ const sh = trackValue("shadow");
12955
+ if (sh !== void 0) {
12956
+ let prevSh;
12957
+ let nextSh;
12958
+ for (const f3 of shadowFrames) {
12959
+ if (f3.at <= t) prevSh = f3;
12960
+ else if (!nextSh) nextSh = f3;
12961
+ }
12962
+ if (prevSh?.shadow && nextSh?.shadow) {
12963
+ const span = nextSh.at - prevSh.at;
12964
+ const k = span > 0 ? (t - prevSh.at) / span : 1;
12965
+ out.shadow = { color: prevSh.shadow.color, blur: lerp(prevSh.shadow.blur, nextSh.shadow.blur, k) };
12966
+ } else {
12967
+ out.shadow = sh;
12968
+ }
12969
+ }
12970
+ }
12971
+ return out;
12972
+ }
12973
+ function gradientStyle(g, ox, oy, scale) {
12974
+ if (g.kind === "linear") {
12975
+ if (!g.from || !g.to) return void 0;
12976
+ return {
12977
+ kind: "linear",
12978
+ x1: ox + g.from.x * scale,
12979
+ y1: oy + g.from.y * scale,
12980
+ x2: ox + g.to.x * scale,
12981
+ y2: oy + g.to.y * scale,
12982
+ stops: g.stops
12983
+ };
12984
+ }
12985
+ if (!g.center || g.radius === void 0) return void 0;
12986
+ return {
12987
+ kind: "radial",
12988
+ cx: ox + g.center.x * scale,
12989
+ cy: oy + g.center.y * scale,
12990
+ r: g.radius * scale,
12991
+ stops: g.stops
12992
+ };
12993
+ }
12910
12994
  function DrawShape(props) {
12911
12995
  const register = React93.useContext(DrawableRegistryContext);
12912
12996
  if (register) {
@@ -12917,25 +13001,51 @@ function DrawShape(props) {
12917
13001
  ...opacity !== void 0 && opacity > 0 ? { opacity } : {}
12918
13002
  };
12919
13003
  register(node);
12920
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-draw-shape-debug": "", style: { position: "absolute", width: 4, height: 4, background: "red", zIndex: 9999 } });
12921
13004
  }
12922
13005
  return null;
12923
13006
  }
12924
- var paintShape;
13007
+ var NUMERIC_TRACKS, lerp, paintShape;
12925
13008
  var init_DrawShape = __esm({
12926
13009
  "components/game/atoms/DrawShape.tsx"() {
12927
13010
  "use client";
12928
13011
  init_contract();
12929
13012
  init_registry();
12930
- paintShape = (painter, node, dctx) => {
13013
+ NUMERIC_TRACKS = [
13014
+ "offsetX",
13015
+ "offsetY",
13016
+ "rotate",
13017
+ "opacity",
13018
+ "radiusX",
13019
+ "radiusY",
13020
+ "width",
13021
+ "height",
13022
+ "strokeWidth"
13023
+ ];
13024
+ lerp = (a, b, k) => a + (b - a) * k;
13025
+ paintShape = (painter, rawNode, dctx) => {
13026
+ const node = dctx.time > 0 ? applyShapeAnimation(rawNode, dctx.time) : rawNode;
12931
13027
  if (!isValidScenePos(node.position)) return;
12932
13028
  painter.save();
12933
13029
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
13030
+ const origin = dctx.projector.project(node.position);
13031
+ const tileWidth = dctx.projector.tileWidth;
13032
+ if (node.rotate) {
13033
+ const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
13034
+ const px = origin.x + pivot.x * tileWidth;
13035
+ const py = origin.y + pivot.y * tileWidth;
13036
+ painter.translate(px, py);
13037
+ painter.rotate(node.rotate);
13038
+ painter.translate(-px, -py);
13039
+ }
13040
+ if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth });
13041
+ const fill = node.fill === "none" ? void 0 : node.fill;
13042
+ const stroke = node.stroke === "none" ? void 0 : node.stroke;
13043
+ const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
12934
13044
  switch (node.shape) {
12935
13045
  case "cell": {
12936
13046
  const pts = dctx.projector.cellPath(node.position);
12937
- if (node.fill) painter.fillPoly(pts, node.fill);
12938
- if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
13047
+ if (pxFill) painter.fillPoly(pts, pxFill);
13048
+ if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
12939
13049
  break;
12940
13050
  }
12941
13051
  case "rect": {
@@ -12945,8 +13055,8 @@ var init_DrawShape = __esm({
12945
13055
  const y = p.y + (node.offsetY ?? 0) * tw;
12946
13056
  const w = (node.width ?? 0) * tw;
12947
13057
  const h = (node.height ?? 0) * tw;
12948
- if (node.fill) painter.fillRect(x, y, w, h, node.fill);
12949
- if (node.stroke) painter.strokeRect(x, y, w, h, node.stroke, node.strokeWidth ?? 1);
13058
+ if (pxFill) painter.fillRect(x, y, w, h, pxFill);
13059
+ if (stroke) painter.strokeRect(x, y, w, h, stroke, node.strokeWidth ?? 1);
12950
13060
  break;
12951
13061
  }
12952
13062
  case "ellipse": {
@@ -12956,26 +13066,26 @@ var init_DrawShape = __esm({
12956
13066
  const cy = p.y + (node.offsetY ?? 0) * tw;
12957
13067
  const rx = (node.radiusX ?? 0) * tw;
12958
13068
  const ry = (node.radiusY ?? rx) * tw;
12959
- if (node.fill) painter.fillEllipse(cx, cy, rx, ry, node.fill);
12960
- if (node.stroke) painter.strokeEllipse(cx, cy, rx, ry, node.stroke, node.strokeWidth ?? 1);
13069
+ if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
13070
+ if (stroke) painter.strokeEllipse(cx, cy, rx, ry, stroke, node.strokeWidth ?? 1);
12961
13071
  break;
12962
13072
  }
12963
13073
  case "poly": {
12964
- const base = dctx.projector.project(node.position);
12965
- const tw = dctx.projector.tileWidth;
12966
- const pts = (node.points ?? []).map((pt) => ({ x: base.x + pt.x * tw, y: base.y + pt.y * tw }));
12967
- if (node.fill) painter.fillPoly(pts, node.fill);
12968
- if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
13074
+ const pts = (node.points ?? []).map((pt) => ({
13075
+ x: origin.x + ((node.offsetX ?? 0) + pt.x) * tileWidth,
13076
+ y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
13077
+ }));
13078
+ if (pxFill) painter.fillPoly(pts, pxFill);
13079
+ if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
12969
13080
  break;
12970
13081
  }
12971
13082
  case "path": {
12972
13083
  if (!node.d) break;
12973
- const base = dctx.projector.project(node.position);
12974
- const tw = dctx.projector.tileWidth;
12975
- painter.translate(base.x, base.y);
12976
- painter.scale(tw, tw);
12977
- if (node.fill) painter.fillPath(node.d, node.fill);
12978
- if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
13084
+ painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
13085
+ painter.scale(tileWidth, tileWidth);
13086
+ const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
13087
+ if (localFill) painter.fillPath(node.d, localFill);
13088
+ if (stroke) painter.strokePath(node.d, stroke, (node.strokeWidth ?? 1) / tileWidth);
12979
13089
  break;
12980
13090
  }
12981
13091
  }
@@ -13187,7 +13297,6 @@ function Canvas2D({
13187
13297
  childDrawablesRef.current.push(node);
13188
13298
  }, []);
13189
13299
  const hasJsxChildren = React93__namespace.Children.count(children) > 0;
13190
- drawables && drawables.length > 0 ? drawables : childDrawablesRef.current;
13191
13300
  function isDrawableLayer(node) {
13192
13301
  return node.type === "draw-sprite-layer" || node.type === "draw-shape-layer" || node.type === "draw-text-layer";
13193
13302
  }
@@ -13336,11 +13445,24 @@ function Canvas2D({
13336
13445
  }, [showMinimap, scenePositions]);
13337
13446
  const miniMapWidth = gridExtent.width || 10;
13338
13447
  const miniMapHeight = gridExtent.height || 10;
13339
- const draw = React93.useCallback(() => {
13448
+ const drawableIsAnimated = (node) => {
13449
+ if (node.type === "draw-shape") return isAnimatedShape(node);
13450
+ if (node.type === "draw-group") return Array.isArray(node.items) && node.items.some(drawableIsAnimated);
13451
+ if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
13452
+ return false;
13453
+ };
13454
+ const animRafRef = React93.useRef(0);
13455
+ const drawTimeRef = React93.useRef(() => void 0);
13456
+ const draw = React93.useCallback((timeMs = 0) => {
13340
13457
  const canvas = canvasRef.current;
13341
13458
  if (!canvas) return;
13342
13459
  const ctx = canvas.getContext("2d");
13343
13460
  if (!ctx) return;
13461
+ const scheduleAnimation = (nodes) => {
13462
+ if (!nodes.some(drawableIsAnimated)) return;
13463
+ cancelAnimationFrame(animRafRef.current);
13464
+ animRafRef.current = requestAnimationFrame(() => drawTimeRef.current(performance.now()));
13465
+ };
13344
13466
  const dpr = window.devicePixelRatio || 1;
13345
13467
  canvas.width = viewportSize.width * dpr;
13346
13468
  canvas.height = viewportSize.height * dpr;
@@ -13373,14 +13495,24 @@ function Canvas2D({
13373
13495
  if (!drawables || drawables.length === 0) {
13374
13496
  const childDrawables = childDrawablesRef.current;
13375
13497
  if (childDrawables.length === 0) return;
13498
+ const cam0 = cameraRef.current;
13499
+ if (camera !== "follow" && dragDistance() === 0) {
13500
+ const focus = cameraPos ?? defaultGridFocus;
13501
+ if (focus) {
13502
+ const p = projector.anchorPoint(focus, "center");
13503
+ cam0.x = p.x - viewportSize.width / 2;
13504
+ cam0.y = p.y - viewportSize.height / 2;
13505
+ }
13506
+ }
13376
13507
  const painter0 = createWebPainter(ctx, bumpAtlas);
13377
13508
  painter0.save();
13378
13509
  painter0.translate(viewportSize.width / 2, viewportSize.height / 2);
13379
- painter0.scale(cameraRef.current.zoom, cameraRef.current.zoom);
13380
- painter0.translate(-viewportSize.width / 2, -viewportSize.height / 2);
13381
- const dctx0 = { projector, time: 0, invalidate: bumpAtlas };
13510
+ painter0.scale(cam0.zoom, cam0.zoom);
13511
+ painter0.translate(-viewportSize.width / 2 - cam0.x, -viewportSize.height / 2 - cam0.y);
13512
+ const dctx0 = { projector, time: timeMs, invalidate: bumpAtlas };
13382
13513
  for (const node of childDrawables) paintDrawable(painter0, node, dctx0);
13383
13514
  painter0.restore();
13515
+ scheduleAnimation(childDrawables);
13384
13516
  return;
13385
13517
  }
13386
13518
  const cam = cameraRef.current;
@@ -13400,10 +13532,15 @@ function Canvas2D({
13400
13532
  painter.translate(viewportSize.width / 2, viewportSize.height / 2);
13401
13533
  painter.scale(cam.zoom, cam.zoom);
13402
13534
  painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
13403
- const dctx = { projector, time: 0, invalidate: bumpAtlas };
13535
+ const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
13404
13536
  for (const node of drawables) paintDrawable(painter, node, dctx);
13405
13537
  painter.restore();
13538
+ scheduleAnimation(drawables);
13406
13539
  }, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
13540
+ React93.useEffect(() => {
13541
+ drawTimeRef.current = draw;
13542
+ }, [draw]);
13543
+ React93.useEffect(() => () => cancelAnimationFrame(animRafRef.current), []);
13407
13544
  React93.useEffect(() => {
13408
13545
  if (camera !== "follow" || !followTarget) return;
13409
13546
  const p = projector.anchorPoint(followTarget, "center");
@@ -13626,15 +13763,7 @@ function Canvas2D({
13626
13763
  mapHeight: miniMapHeight
13627
13764
  }
13628
13765
  ) }),
13629
- hasJsxChildren && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children }),
13630
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-debug": "", style: { position: "absolute", top: 0, left: 0, background: "yellow", color: "black", zIndex: 9999, fontSize: 24, padding: 8 }, children: [
13631
- "C=",
13632
- React93__namespace.Children.count(children),
13633
- " J=",
13634
- String(hasJsxChildren),
13635
- " D=",
13636
- drawables?.length ?? -1
13637
- ] })
13766
+ hasJsxChildren && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children })
13638
13767
  ]
13639
13768
  }
13640
13769
  ) });
@@ -13659,6 +13788,7 @@ var init_Canvas2D = __esm({
13659
13788
  init_webPainter2d();
13660
13789
  init_projector();
13661
13790
  init_paintDispatch();
13791
+ init_DrawShape();
13662
13792
  init_registry();
13663
13793
  init_hitTest();
13664
13794
  init_isometric();
@@ -47563,8 +47693,13 @@ function SlotContentRenderer({
47563
47693
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
47564
47694
  const hasChildren = PATTERNS_WITH_CHILDREN.has(content.pattern) || Array.isArray(childrenConfig) && childrenConfig.length > 0 || isSingleChild;
47565
47695
  const isDrawHost = patterns.isDrawHostPattern(content.pattern);
47696
+ const arr = Array.isArray(childrenConfig) ? childrenConfig : childrenConfig ? [childrenConfig] : [];
47697
+ const hasTraitChildren = arr.some(
47698
+ (c) => typeof c === "string" && TRAIT_BINDING_RE.test(c)
47699
+ );
47700
+ const drawHostUsesReactChildren = isDrawHost && hasTraitChildren;
47566
47701
  const myPath = patternPath ?? "root";
47567
- const renderedChildren = hasChildren && !isDrawHost ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath, content.sourceTrait, {
47702
+ const renderedChildren = hasChildren && (!isDrawHost || drawHostUsesReactChildren) ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath, content.sourceTrait, {
47568
47703
  slot: content.slot,
47569
47704
  transitionEvent: content.transitionEvent,
47570
47705
  fromState: content.fromState,
@@ -47625,7 +47760,7 @@ function SlotContentRenderer({
47625
47760
  for (const [k, v] of Object.entries(nodeSlotOverrides)) {
47626
47761
  finalProps[k] = v;
47627
47762
  }
47628
- if (isDrawHost && Array.isArray(childrenConfig) && childrenConfig.length > 0) {
47763
+ if (isDrawHost && !drawHostUsesReactChildren && Array.isArray(childrenConfig) && childrenConfig.length > 0) {
47629
47764
  finalProps.drawables = toDrawableNodes(childrenConfig);
47630
47765
  }
47631
47766
  const entityVal = finalProps.entity;
@@ -47804,6 +47939,8 @@ var init_UISlotRenderer = __esm({
47804
47939
  "vstack",
47805
47940
  "hstack",
47806
47941
  "box",
47942
+ "canvas",
47943
+ "canvas-2d",
47807
47944
  "grid",
47808
47945
  "center",
47809
47946
  "card",