@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.
@@ -8823,6 +8823,12 @@ function createWebPainter(ctx, onAssetLoad) {
8823
8823
  for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
8824
8824
  if (closed) ctx.closePath();
8825
8825
  };
8826
+ const toCanvasStyle = (style) => {
8827
+ if (typeof style === "string") return style;
8828
+ 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);
8829
+ for (const stop of style.stops) g.addColorStop(stop.offset, stop.color);
8830
+ return g;
8831
+ };
8826
8832
  return {
8827
8833
  setViewport(width, height, dpr) {
8828
8834
  vw = width;
@@ -8876,47 +8882,47 @@ function createWebPainter(ctx, onAssetLoad) {
8876
8882
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
8877
8883
  }
8878
8884
  },
8879
- fillRect(x, y, w, h, color) {
8880
- ctx.fillStyle = color;
8885
+ fillRect(x, y, w, h, style) {
8886
+ ctx.fillStyle = toCanvasStyle(style);
8881
8887
  ctx.fillRect(x, y, w, h);
8882
8888
  },
8883
- strokeRect(x, y, w, h, color, lineWidth = 1) {
8884
- ctx.strokeStyle = color;
8889
+ strokeRect(x, y, w, h, style, lineWidth = 1) {
8890
+ ctx.strokeStyle = toCanvasStyle(style);
8885
8891
  ctx.lineWidth = lineWidth;
8886
8892
  ctx.strokeRect(x, y, w, h);
8887
8893
  },
8888
- fillPoly(points, color) {
8894
+ fillPoly(points, style) {
8889
8895
  if (points.length === 0) return;
8890
8896
  tracePoly(points, true);
8891
- ctx.fillStyle = color;
8897
+ ctx.fillStyle = toCanvasStyle(style);
8892
8898
  ctx.fill();
8893
8899
  },
8894
- strokePoly(points, color, lineWidth = 1, closed = false) {
8900
+ strokePoly(points, style, lineWidth = 1, closed = false) {
8895
8901
  if (points.length === 0) return;
8896
8902
  tracePoly(points, closed);
8897
- ctx.strokeStyle = color;
8903
+ ctx.strokeStyle = toCanvasStyle(style);
8898
8904
  ctx.lineWidth = lineWidth;
8899
8905
  ctx.stroke();
8900
8906
  },
8901
- fillEllipse(cx, cy, rx, ry, color) {
8907
+ fillEllipse(cx, cy, rx, ry, style) {
8902
8908
  ctx.beginPath();
8903
8909
  ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
8904
- ctx.fillStyle = color;
8910
+ ctx.fillStyle = toCanvasStyle(style);
8905
8911
  ctx.fill();
8906
8912
  },
8907
- strokeEllipse(cx, cy, rx, ry, color, lineWidth = 1) {
8913
+ strokeEllipse(cx, cy, rx, ry, style, lineWidth = 1) {
8908
8914
  ctx.beginPath();
8909
8915
  ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
8910
- ctx.strokeStyle = color;
8916
+ ctx.strokeStyle = toCanvasStyle(style);
8911
8917
  ctx.lineWidth = lineWidth;
8912
8918
  ctx.stroke();
8913
8919
  },
8914
- fillPath(d, color) {
8915
- ctx.fillStyle = color;
8920
+ fillPath(d, style) {
8921
+ ctx.fillStyle = toCanvasStyle(style);
8916
8922
  ctx.fill(new Path2D(d));
8917
8923
  },
8918
- strokePath(d, color, lineWidth = 1) {
8919
- ctx.strokeStyle = color;
8924
+ strokePath(d, style, lineWidth = 1) {
8925
+ ctx.strokeStyle = toCanvasStyle(style);
8920
8926
  ctx.lineWidth = lineWidth;
8921
8927
  ctx.stroke(new Path2D(d));
8922
8928
  },
@@ -9091,6 +9097,84 @@ var init_registry = __esm({
9091
9097
  DrawableRegistryContext = createContext(null);
9092
9098
  }
9093
9099
  });
9100
+ function isAnimatedShape(node) {
9101
+ return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
9102
+ }
9103
+ function applyShapeAnimation(node, timeMs) {
9104
+ const anim = node.animation;
9105
+ if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
9106
+ const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
9107
+ const cycle = timeMs / anim.durationMs;
9108
+ const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
9109
+ const out = { ...node };
9110
+ const trackValue = (key) => {
9111
+ const defined = frames.filter((f3) => f3[key] !== void 0);
9112
+ if (defined.length === 0) return void 0;
9113
+ let prev;
9114
+ let next;
9115
+ for (const f3 of defined) {
9116
+ if (f3.at <= t) prev = f3;
9117
+ else if (!next) next = f3;
9118
+ }
9119
+ if (!prev) return defined[0][key];
9120
+ if (!next) return prev[key];
9121
+ const span = next.at - prev.at;
9122
+ const k = span > 0 ? (t - prev.at) / span : 1;
9123
+ const a = prev[key];
9124
+ const b = next[key];
9125
+ if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
9126
+ return a;
9127
+ };
9128
+ for (const key of NUMERIC_TRACKS) {
9129
+ const v = trackValue(key);
9130
+ if (v !== void 0) out[key] = v;
9131
+ }
9132
+ const fill = trackValue("fill");
9133
+ if (fill !== void 0) out.fill = fill;
9134
+ const stroke = trackValue("stroke");
9135
+ if (stroke !== void 0) out.stroke = stroke;
9136
+ const shadowFrames = frames.filter((f3) => f3.shadow !== void 0);
9137
+ if (shadowFrames.length > 0) {
9138
+ const sh = trackValue("shadow");
9139
+ if (sh !== void 0) {
9140
+ let prevSh;
9141
+ let nextSh;
9142
+ for (const f3 of shadowFrames) {
9143
+ if (f3.at <= t) prevSh = f3;
9144
+ else if (!nextSh) nextSh = f3;
9145
+ }
9146
+ if (prevSh?.shadow && nextSh?.shadow) {
9147
+ const span = nextSh.at - prevSh.at;
9148
+ const k = span > 0 ? (t - prevSh.at) / span : 1;
9149
+ out.shadow = { color: prevSh.shadow.color, blur: lerp(prevSh.shadow.blur, nextSh.shadow.blur, k) };
9150
+ } else {
9151
+ out.shadow = sh;
9152
+ }
9153
+ }
9154
+ }
9155
+ return out;
9156
+ }
9157
+ function gradientStyle(g, ox, oy, scale) {
9158
+ if (g.kind === "linear") {
9159
+ if (!g.from || !g.to) return void 0;
9160
+ return {
9161
+ kind: "linear",
9162
+ x1: ox + g.from.x * scale,
9163
+ y1: oy + g.from.y * scale,
9164
+ x2: ox + g.to.x * scale,
9165
+ y2: oy + g.to.y * scale,
9166
+ stops: g.stops
9167
+ };
9168
+ }
9169
+ if (!g.center || g.radius === void 0) return void 0;
9170
+ return {
9171
+ kind: "radial",
9172
+ cx: ox + g.center.x * scale,
9173
+ cy: oy + g.center.y * scale,
9174
+ r: g.radius * scale,
9175
+ stops: g.stops
9176
+ };
9177
+ }
9094
9178
  function DrawShape(props) {
9095
9179
  const register = useContext(DrawableRegistryContext);
9096
9180
  if (register) {
@@ -9101,25 +9185,51 @@ function DrawShape(props) {
9101
9185
  ...opacity !== void 0 && opacity > 0 ? { opacity } : {}
9102
9186
  };
9103
9187
  register(node);
9104
- return /* @__PURE__ */ jsx("div", { "data-draw-shape-debug": "", style: { position: "absolute", width: 4, height: 4, background: "red", zIndex: 9999 } });
9105
9188
  }
9106
9189
  return null;
9107
9190
  }
9108
- var paintShape;
9191
+ var NUMERIC_TRACKS, lerp, paintShape;
9109
9192
  var init_DrawShape = __esm({
9110
9193
  "components/game/atoms/DrawShape.tsx"() {
9111
9194
  "use client";
9112
9195
  init_contract();
9113
9196
  init_registry();
9114
- paintShape = (painter, node, dctx) => {
9197
+ NUMERIC_TRACKS = [
9198
+ "offsetX",
9199
+ "offsetY",
9200
+ "rotate",
9201
+ "opacity",
9202
+ "radiusX",
9203
+ "radiusY",
9204
+ "width",
9205
+ "height",
9206
+ "strokeWidth"
9207
+ ];
9208
+ lerp = (a, b, k) => a + (b - a) * k;
9209
+ paintShape = (painter, rawNode, dctx) => {
9210
+ const node = dctx.time > 0 ? applyShapeAnimation(rawNode, dctx.time) : rawNode;
9115
9211
  if (!isValidScenePos(node.position)) return;
9116
9212
  painter.save();
9117
9213
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
9214
+ const origin = dctx.projector.project(node.position);
9215
+ const tileWidth = dctx.projector.tileWidth;
9216
+ if (node.rotate) {
9217
+ const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
9218
+ const px = origin.x + pivot.x * tileWidth;
9219
+ const py = origin.y + pivot.y * tileWidth;
9220
+ painter.translate(px, py);
9221
+ painter.rotate(node.rotate);
9222
+ painter.translate(-px, -py);
9223
+ }
9224
+ if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth });
9225
+ const fill = node.fill === "none" ? void 0 : node.fill;
9226
+ const stroke = node.stroke === "none" ? void 0 : node.stroke;
9227
+ const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
9118
9228
  switch (node.shape) {
9119
9229
  case "cell": {
9120
9230
  const pts = dctx.projector.cellPath(node.position);
9121
- if (node.fill) painter.fillPoly(pts, node.fill);
9122
- if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
9231
+ if (pxFill) painter.fillPoly(pts, pxFill);
9232
+ if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
9123
9233
  break;
9124
9234
  }
9125
9235
  case "rect": {
@@ -9129,8 +9239,8 @@ var init_DrawShape = __esm({
9129
9239
  const y = p.y + (node.offsetY ?? 0) * tw;
9130
9240
  const w = (node.width ?? 0) * tw;
9131
9241
  const h = (node.height ?? 0) * tw;
9132
- if (node.fill) painter.fillRect(x, y, w, h, node.fill);
9133
- if (node.stroke) painter.strokeRect(x, y, w, h, node.stroke, node.strokeWidth ?? 1);
9242
+ if (pxFill) painter.fillRect(x, y, w, h, pxFill);
9243
+ if (stroke) painter.strokeRect(x, y, w, h, stroke, node.strokeWidth ?? 1);
9134
9244
  break;
9135
9245
  }
9136
9246
  case "ellipse": {
@@ -9140,26 +9250,26 @@ var init_DrawShape = __esm({
9140
9250
  const cy = p.y + (node.offsetY ?? 0) * tw;
9141
9251
  const rx = (node.radiusX ?? 0) * tw;
9142
9252
  const ry = (node.radiusY ?? rx) * tw;
9143
- if (node.fill) painter.fillEllipse(cx, cy, rx, ry, node.fill);
9144
- if (node.stroke) painter.strokeEllipse(cx, cy, rx, ry, node.stroke, node.strokeWidth ?? 1);
9253
+ if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
9254
+ if (stroke) painter.strokeEllipse(cx, cy, rx, ry, stroke, node.strokeWidth ?? 1);
9145
9255
  break;
9146
9256
  }
9147
9257
  case "poly": {
9148
- const base = dctx.projector.project(node.position);
9149
- const tw = dctx.projector.tileWidth;
9150
- const pts = (node.points ?? []).map((pt) => ({ x: base.x + pt.x * tw, y: base.y + pt.y * tw }));
9151
- if (node.fill) painter.fillPoly(pts, node.fill);
9152
- if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
9258
+ const pts = (node.points ?? []).map((pt) => ({
9259
+ x: origin.x + ((node.offsetX ?? 0) + pt.x) * tileWidth,
9260
+ y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
9261
+ }));
9262
+ if (pxFill) painter.fillPoly(pts, pxFill);
9263
+ if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
9153
9264
  break;
9154
9265
  }
9155
9266
  case "path": {
9156
9267
  if (!node.d) break;
9157
- const base = dctx.projector.project(node.position);
9158
- const tw = dctx.projector.tileWidth;
9159
- painter.translate(base.x, base.y);
9160
- painter.scale(tw, tw);
9161
- if (node.fill) painter.fillPath(node.d, node.fill);
9162
- if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
9268
+ painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
9269
+ painter.scale(tileWidth, tileWidth);
9270
+ const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
9271
+ if (localFill) painter.fillPath(node.d, localFill);
9272
+ if (stroke) painter.strokePath(node.d, stroke, (node.strokeWidth ?? 1) / tileWidth);
9163
9273
  break;
9164
9274
  }
9165
9275
  }
@@ -9371,7 +9481,6 @@ function Canvas2D({
9371
9481
  childDrawablesRef.current.push(node);
9372
9482
  }, []);
9373
9483
  const hasJsxChildren = React86.Children.count(children) > 0;
9374
- drawables && drawables.length > 0 ? drawables : childDrawablesRef.current;
9375
9484
  function isDrawableLayer(node) {
9376
9485
  return node.type === "draw-sprite-layer" || node.type === "draw-shape-layer" || node.type === "draw-text-layer";
9377
9486
  }
@@ -9520,11 +9629,24 @@ function Canvas2D({
9520
9629
  }, [showMinimap, scenePositions]);
9521
9630
  const miniMapWidth = gridExtent.width || 10;
9522
9631
  const miniMapHeight = gridExtent.height || 10;
9523
- const draw = useCallback(() => {
9632
+ const drawableIsAnimated = (node) => {
9633
+ if (node.type === "draw-shape") return isAnimatedShape(node);
9634
+ if (node.type === "draw-group") return Array.isArray(node.items) && node.items.some(drawableIsAnimated);
9635
+ if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
9636
+ return false;
9637
+ };
9638
+ const animRafRef = useRef(0);
9639
+ const drawTimeRef = useRef(() => void 0);
9640
+ const draw = useCallback((timeMs = 0) => {
9524
9641
  const canvas = canvasRef.current;
9525
9642
  if (!canvas) return;
9526
9643
  const ctx = canvas.getContext("2d");
9527
9644
  if (!ctx) return;
9645
+ const scheduleAnimation = (nodes) => {
9646
+ if (!nodes.some(drawableIsAnimated)) return;
9647
+ cancelAnimationFrame(animRafRef.current);
9648
+ animRafRef.current = requestAnimationFrame(() => drawTimeRef.current(performance.now()));
9649
+ };
9528
9650
  const dpr = window.devicePixelRatio || 1;
9529
9651
  canvas.width = viewportSize.width * dpr;
9530
9652
  canvas.height = viewportSize.height * dpr;
@@ -9557,14 +9679,24 @@ function Canvas2D({
9557
9679
  if (!drawables || drawables.length === 0) {
9558
9680
  const childDrawables = childDrawablesRef.current;
9559
9681
  if (childDrawables.length === 0) return;
9682
+ const cam0 = cameraRef.current;
9683
+ if (camera !== "follow" && dragDistance() === 0) {
9684
+ const focus = cameraPos ?? defaultGridFocus;
9685
+ if (focus) {
9686
+ const p = projector.anchorPoint(focus, "center");
9687
+ cam0.x = p.x - viewportSize.width / 2;
9688
+ cam0.y = p.y - viewportSize.height / 2;
9689
+ }
9690
+ }
9560
9691
  const painter0 = createWebPainter(ctx, bumpAtlas);
9561
9692
  painter0.save();
9562
9693
  painter0.translate(viewportSize.width / 2, viewportSize.height / 2);
9563
- painter0.scale(cameraRef.current.zoom, cameraRef.current.zoom);
9564
- painter0.translate(-viewportSize.width / 2, -viewportSize.height / 2);
9565
- const dctx0 = { projector, time: 0, invalidate: bumpAtlas };
9694
+ painter0.scale(cam0.zoom, cam0.zoom);
9695
+ painter0.translate(-viewportSize.width / 2 - cam0.x, -viewportSize.height / 2 - cam0.y);
9696
+ const dctx0 = { projector, time: timeMs, invalidate: bumpAtlas };
9566
9697
  for (const node of childDrawables) paintDrawable(painter0, node, dctx0);
9567
9698
  painter0.restore();
9699
+ scheduleAnimation(childDrawables);
9568
9700
  return;
9569
9701
  }
9570
9702
  const cam = cameraRef.current;
@@ -9584,10 +9716,15 @@ function Canvas2D({
9584
9716
  painter.translate(viewportSize.width / 2, viewportSize.height / 2);
9585
9717
  painter.scale(cam.zoom, cam.zoom);
9586
9718
  painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
9587
- const dctx = { projector, time: 0, invalidate: bumpAtlas };
9719
+ const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
9588
9720
  for (const node of drawables) paintDrawable(painter, node, dctx);
9589
9721
  painter.restore();
9722
+ scheduleAnimation(drawables);
9590
9723
  }, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
9724
+ useEffect(() => {
9725
+ drawTimeRef.current = draw;
9726
+ }, [draw]);
9727
+ useEffect(() => () => cancelAnimationFrame(animRafRef.current), []);
9591
9728
  useEffect(() => {
9592
9729
  if (camera !== "follow" || !followTarget) return;
9593
9730
  const p = projector.anchorPoint(followTarget, "center");
@@ -9810,15 +9947,7 @@ function Canvas2D({
9810
9947
  mapHeight: miniMapHeight
9811
9948
  }
9812
9949
  ) }),
9813
- hasJsxChildren && /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children }),
9814
- /* @__PURE__ */ jsxs("div", { "data-debug": "", style: { position: "absolute", top: 0, left: 0, background: "yellow", color: "black", zIndex: 9999, fontSize: 24, padding: 8 }, children: [
9815
- "C=",
9816
- React86.Children.count(children),
9817
- " J=",
9818
- String(hasJsxChildren),
9819
- " D=",
9820
- drawables?.length ?? -1
9821
- ] })
9950
+ hasJsxChildren && /* @__PURE__ */ jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children })
9822
9951
  ]
9823
9952
  }
9824
9953
  ) });
@@ -9843,6 +9972,7 @@ var init_Canvas2D = __esm({
9843
9972
  init_webPainter2d();
9844
9973
  init_projector();
9845
9974
  init_paintDispatch();
9975
+ init_DrawShape();
9846
9976
  init_registry();
9847
9977
  init_hitTest();
9848
9978
  init_isometric();
@@ -45614,8 +45744,13 @@ function SlotContentRenderer({
45614
45744
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
45615
45745
  const hasChildren = PATTERNS_WITH_CHILDREN.has(content.pattern) || Array.isArray(childrenConfig) && childrenConfig.length > 0 || isSingleChild;
45616
45746
  const isDrawHost = isDrawHostPattern(content.pattern);
45747
+ const arr = Array.isArray(childrenConfig) ? childrenConfig : childrenConfig ? [childrenConfig] : [];
45748
+ const hasTraitChildren = arr.some(
45749
+ (c) => typeof c === "string" && TRAIT_BINDING_RE.test(c)
45750
+ );
45751
+ const drawHostUsesReactChildren = isDrawHost && hasTraitChildren;
45617
45752
  const myPath = patternPath ?? "root";
45618
- const renderedChildren = hasChildren && !isDrawHost ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath, content.sourceTrait, {
45753
+ const renderedChildren = hasChildren && (!isDrawHost || drawHostUsesReactChildren) ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath, content.sourceTrait, {
45619
45754
  slot: content.slot,
45620
45755
  transitionEvent: content.transitionEvent,
45621
45756
  fromState: content.fromState,
@@ -45676,7 +45811,7 @@ function SlotContentRenderer({
45676
45811
  for (const [k, v] of Object.entries(nodeSlotOverrides)) {
45677
45812
  finalProps[k] = v;
45678
45813
  }
45679
- if (isDrawHost && Array.isArray(childrenConfig) && childrenConfig.length > 0) {
45814
+ if (isDrawHost && !drawHostUsesReactChildren && Array.isArray(childrenConfig) && childrenConfig.length > 0) {
45680
45815
  finalProps.drawables = toDrawableNodes(childrenConfig);
45681
45816
  }
45682
45817
  const entityVal = finalProps.entity;
@@ -45855,6 +45990,8 @@ var init_UISlotRenderer = __esm({
45855
45990
  "vstack",
45856
45991
  "hstack",
45857
45992
  "box",
45993
+ "canvas",
45994
+ "canvas-2d",
45858
45995
  "grid",
45859
45996
  "center",
45860
45997
  "card",