@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.
@@ -8897,6 +8897,12 @@ function createWebPainter(ctx, onAssetLoad) {
8897
8897
  for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
8898
8898
  if (closed) ctx.closePath();
8899
8899
  };
8900
+ const toCanvasStyle = (style) => {
8901
+ if (typeof style === "string") return style;
8902
+ 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);
8903
+ for (const stop of style.stops) g.addColorStop(stop.offset, stop.color);
8904
+ return g;
8905
+ };
8900
8906
  return {
8901
8907
  setViewport(width, height, dpr) {
8902
8908
  vw = width;
@@ -8950,47 +8956,47 @@ function createWebPainter(ctx, onAssetLoad) {
8950
8956
  ctx.drawImage(img, dest.x, dest.y, dw, dh);
8951
8957
  }
8952
8958
  },
8953
- fillRect(x, y, w, h, color) {
8954
- ctx.fillStyle = color;
8959
+ fillRect(x, y, w, h, style) {
8960
+ ctx.fillStyle = toCanvasStyle(style);
8955
8961
  ctx.fillRect(x, y, w, h);
8956
8962
  },
8957
- strokeRect(x, y, w, h, color, lineWidth = 1) {
8958
- ctx.strokeStyle = color;
8963
+ strokeRect(x, y, w, h, style, lineWidth = 1) {
8964
+ ctx.strokeStyle = toCanvasStyle(style);
8959
8965
  ctx.lineWidth = lineWidth;
8960
8966
  ctx.strokeRect(x, y, w, h);
8961
8967
  },
8962
- fillPoly(points, color) {
8968
+ fillPoly(points, style) {
8963
8969
  if (points.length === 0) return;
8964
8970
  tracePoly(points, true);
8965
- ctx.fillStyle = color;
8971
+ ctx.fillStyle = toCanvasStyle(style);
8966
8972
  ctx.fill();
8967
8973
  },
8968
- strokePoly(points, color, lineWidth = 1, closed = false) {
8974
+ strokePoly(points, style, lineWidth = 1, closed = false) {
8969
8975
  if (points.length === 0) return;
8970
8976
  tracePoly(points, closed);
8971
- ctx.strokeStyle = color;
8977
+ ctx.strokeStyle = toCanvasStyle(style);
8972
8978
  ctx.lineWidth = lineWidth;
8973
8979
  ctx.stroke();
8974
8980
  },
8975
- fillEllipse(cx, cy, rx, ry, color) {
8981
+ fillEllipse(cx, cy, rx, ry, style) {
8976
8982
  ctx.beginPath();
8977
8983
  ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
8978
- ctx.fillStyle = color;
8984
+ ctx.fillStyle = toCanvasStyle(style);
8979
8985
  ctx.fill();
8980
8986
  },
8981
- strokeEllipse(cx, cy, rx, ry, color, lineWidth = 1) {
8987
+ strokeEllipse(cx, cy, rx, ry, style, lineWidth = 1) {
8982
8988
  ctx.beginPath();
8983
8989
  ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
8984
- ctx.strokeStyle = color;
8990
+ ctx.strokeStyle = toCanvasStyle(style);
8985
8991
  ctx.lineWidth = lineWidth;
8986
8992
  ctx.stroke();
8987
8993
  },
8988
- fillPath(d, color) {
8989
- ctx.fillStyle = color;
8994
+ fillPath(d, style) {
8995
+ ctx.fillStyle = toCanvasStyle(style);
8990
8996
  ctx.fill(new Path2D(d));
8991
8997
  },
8992
- strokePath(d, color, lineWidth = 1) {
8993
- ctx.strokeStyle = color;
8998
+ strokePath(d, style, lineWidth = 1) {
8999
+ ctx.strokeStyle = toCanvasStyle(style);
8994
9000
  ctx.lineWidth = lineWidth;
8995
9001
  ctx.stroke(new Path2D(d));
8996
9002
  },
@@ -9165,6 +9171,84 @@ var init_registry = __esm({
9165
9171
  DrawableRegistryContext = React86.createContext(null);
9166
9172
  }
9167
9173
  });
9174
+ function isAnimatedShape(node) {
9175
+ return Boolean(node.animation && node.animation.durationMs > 0 && node.animation.keyframes.length > 0);
9176
+ }
9177
+ function applyShapeAnimation(node, timeMs) {
9178
+ const anim = node.animation;
9179
+ if (!anim || !(anim.durationMs > 0) || anim.keyframes.length === 0) return node;
9180
+ const frames = [...anim.keyframes].sort((a, b) => a.at - b.at);
9181
+ const cycle = timeMs / anim.durationMs;
9182
+ const t = anim.loop === false ? Math.min(cycle, 1) : cycle - Math.floor(cycle);
9183
+ const out = { ...node };
9184
+ const trackValue = (key) => {
9185
+ const defined = frames.filter((f3) => f3[key] !== void 0);
9186
+ if (defined.length === 0) return void 0;
9187
+ let prev;
9188
+ let next;
9189
+ for (const f3 of defined) {
9190
+ if (f3.at <= t) prev = f3;
9191
+ else if (!next) next = f3;
9192
+ }
9193
+ if (!prev) return defined[0][key];
9194
+ if (!next) return prev[key];
9195
+ const span = next.at - prev.at;
9196
+ const k = span > 0 ? (t - prev.at) / span : 1;
9197
+ const a = prev[key];
9198
+ const b = next[key];
9199
+ if (typeof a === "number" && typeof b === "number") return lerp(a, b, k);
9200
+ return a;
9201
+ };
9202
+ for (const key of NUMERIC_TRACKS) {
9203
+ const v = trackValue(key);
9204
+ if (v !== void 0) out[key] = v;
9205
+ }
9206
+ const fill = trackValue("fill");
9207
+ if (fill !== void 0) out.fill = fill;
9208
+ const stroke = trackValue("stroke");
9209
+ if (stroke !== void 0) out.stroke = stroke;
9210
+ const shadowFrames = frames.filter((f3) => f3.shadow !== void 0);
9211
+ if (shadowFrames.length > 0) {
9212
+ const sh = trackValue("shadow");
9213
+ if (sh !== void 0) {
9214
+ let prevSh;
9215
+ let nextSh;
9216
+ for (const f3 of shadowFrames) {
9217
+ if (f3.at <= t) prevSh = f3;
9218
+ else if (!nextSh) nextSh = f3;
9219
+ }
9220
+ if (prevSh?.shadow && nextSh?.shadow) {
9221
+ const span = nextSh.at - prevSh.at;
9222
+ const k = span > 0 ? (t - prevSh.at) / span : 1;
9223
+ out.shadow = { color: prevSh.shadow.color, blur: lerp(prevSh.shadow.blur, nextSh.shadow.blur, k) };
9224
+ } else {
9225
+ out.shadow = sh;
9226
+ }
9227
+ }
9228
+ }
9229
+ return out;
9230
+ }
9231
+ function gradientStyle(g, ox, oy, scale) {
9232
+ if (g.kind === "linear") {
9233
+ if (!g.from || !g.to) return void 0;
9234
+ return {
9235
+ kind: "linear",
9236
+ x1: ox + g.from.x * scale,
9237
+ y1: oy + g.from.y * scale,
9238
+ x2: ox + g.to.x * scale,
9239
+ y2: oy + g.to.y * scale,
9240
+ stops: g.stops
9241
+ };
9242
+ }
9243
+ if (!g.center || g.radius === void 0) return void 0;
9244
+ return {
9245
+ kind: "radial",
9246
+ cx: ox + g.center.x * scale,
9247
+ cy: oy + g.center.y * scale,
9248
+ r: g.radius * scale,
9249
+ stops: g.stops
9250
+ };
9251
+ }
9168
9252
  function DrawShape(props) {
9169
9253
  const register = React86.useContext(DrawableRegistryContext);
9170
9254
  if (register) {
@@ -9175,25 +9259,51 @@ function DrawShape(props) {
9175
9259
  ...opacity !== void 0 && opacity > 0 ? { opacity } : {}
9176
9260
  };
9177
9261
  register(node);
9178
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-draw-shape-debug": "", style: { position: "absolute", width: 4, height: 4, background: "red", zIndex: 9999 } });
9179
9262
  }
9180
9263
  return null;
9181
9264
  }
9182
- var paintShape;
9265
+ var NUMERIC_TRACKS, lerp, paintShape;
9183
9266
  var init_DrawShape = __esm({
9184
9267
  "components/game/atoms/DrawShape.tsx"() {
9185
9268
  "use client";
9186
9269
  init_contract();
9187
9270
  init_registry();
9188
- paintShape = (painter, node, dctx) => {
9271
+ NUMERIC_TRACKS = [
9272
+ "offsetX",
9273
+ "offsetY",
9274
+ "rotate",
9275
+ "opacity",
9276
+ "radiusX",
9277
+ "radiusY",
9278
+ "width",
9279
+ "height",
9280
+ "strokeWidth"
9281
+ ];
9282
+ lerp = (a, b, k) => a + (b - a) * k;
9283
+ paintShape = (painter, rawNode, dctx) => {
9284
+ const node = dctx.time > 0 ? applyShapeAnimation(rawNode, dctx.time) : rawNode;
9189
9285
  if (!isValidScenePos(node.position)) return;
9190
9286
  painter.save();
9191
9287
  if (node.opacity !== void 0 && node.opacity !== 1) painter.setAlpha(node.opacity);
9288
+ const origin = dctx.projector.project(node.position);
9289
+ const tileWidth = dctx.projector.tileWidth;
9290
+ if (node.rotate) {
9291
+ const pivot = node.pivot ?? { x: 0.5, y: 0.5 };
9292
+ const px = origin.x + pivot.x * tileWidth;
9293
+ const py = origin.y + pivot.y * tileWidth;
9294
+ painter.translate(px, py);
9295
+ painter.rotate(node.rotate);
9296
+ painter.translate(-px, -py);
9297
+ }
9298
+ if (node.shadow) painter.setShadow({ color: node.shadow.color, blur: node.shadow.blur * tileWidth });
9299
+ const fill = node.fill === "none" ? void 0 : node.fill;
9300
+ const stroke = node.stroke === "none" ? void 0 : node.stroke;
9301
+ const pxFill = node.gradient ? gradientStyle(node.gradient, origin.x, origin.y, tileWidth) ?? fill : fill;
9192
9302
  switch (node.shape) {
9193
9303
  case "cell": {
9194
9304
  const pts = dctx.projector.cellPath(node.position);
9195
- if (node.fill) painter.fillPoly(pts, node.fill);
9196
- if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
9305
+ if (pxFill) painter.fillPoly(pts, pxFill);
9306
+ if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
9197
9307
  break;
9198
9308
  }
9199
9309
  case "rect": {
@@ -9203,8 +9313,8 @@ var init_DrawShape = __esm({
9203
9313
  const y = p.y + (node.offsetY ?? 0) * tw;
9204
9314
  const w = (node.width ?? 0) * tw;
9205
9315
  const h = (node.height ?? 0) * tw;
9206
- if (node.fill) painter.fillRect(x, y, w, h, node.fill);
9207
- if (node.stroke) painter.strokeRect(x, y, w, h, node.stroke, node.strokeWidth ?? 1);
9316
+ if (pxFill) painter.fillRect(x, y, w, h, pxFill);
9317
+ if (stroke) painter.strokeRect(x, y, w, h, stroke, node.strokeWidth ?? 1);
9208
9318
  break;
9209
9319
  }
9210
9320
  case "ellipse": {
@@ -9214,26 +9324,26 @@ var init_DrawShape = __esm({
9214
9324
  const cy = p.y + (node.offsetY ?? 0) * tw;
9215
9325
  const rx = (node.radiusX ?? 0) * tw;
9216
9326
  const ry = (node.radiusY ?? rx) * tw;
9217
- if (node.fill) painter.fillEllipse(cx, cy, rx, ry, node.fill);
9218
- if (node.stroke) painter.strokeEllipse(cx, cy, rx, ry, node.stroke, node.strokeWidth ?? 1);
9327
+ if (pxFill) painter.fillEllipse(cx, cy, rx, ry, pxFill);
9328
+ if (stroke) painter.strokeEllipse(cx, cy, rx, ry, stroke, node.strokeWidth ?? 1);
9219
9329
  break;
9220
9330
  }
9221
9331
  case "poly": {
9222
- const base = dctx.projector.project(node.position);
9223
- const tw = dctx.projector.tileWidth;
9224
- const pts = (node.points ?? []).map((pt) => ({ x: base.x + pt.x * tw, y: base.y + pt.y * tw }));
9225
- if (node.fill) painter.fillPoly(pts, node.fill);
9226
- if (node.stroke) painter.strokePoly(pts, node.stroke, node.strokeWidth ?? 1, true);
9332
+ const pts = (node.points ?? []).map((pt) => ({
9333
+ x: origin.x + ((node.offsetX ?? 0) + pt.x) * tileWidth,
9334
+ y: origin.y + ((node.offsetY ?? 0) + pt.y) * tileWidth
9335
+ }));
9336
+ if (pxFill) painter.fillPoly(pts, pxFill);
9337
+ if (stroke) painter.strokePoly(pts, stroke, node.strokeWidth ?? 1, true);
9227
9338
  break;
9228
9339
  }
9229
9340
  case "path": {
9230
9341
  if (!node.d) break;
9231
- const base = dctx.projector.project(node.position);
9232
- const tw = dctx.projector.tileWidth;
9233
- painter.translate(base.x, base.y);
9234
- painter.scale(tw, tw);
9235
- if (node.fill) painter.fillPath(node.d, node.fill);
9236
- if (node.stroke) painter.strokePath(node.d, node.stroke, (node.strokeWidth ?? 1) / tw);
9342
+ painter.translate(origin.x + (node.offsetX ?? 0) * tileWidth, origin.y + (node.offsetY ?? 0) * tileWidth);
9343
+ painter.scale(tileWidth, tileWidth);
9344
+ const localFill = node.gradient ? gradientStyle(node.gradient, 0, 0, 1) ?? fill : fill;
9345
+ if (localFill) painter.fillPath(node.d, localFill);
9346
+ if (stroke) painter.strokePath(node.d, stroke, (node.strokeWidth ?? 1) / tileWidth);
9237
9347
  break;
9238
9348
  }
9239
9349
  }
@@ -9445,7 +9555,6 @@ function Canvas2D({
9445
9555
  childDrawablesRef.current.push(node);
9446
9556
  }, []);
9447
9557
  const hasJsxChildren = React86__namespace.Children.count(children) > 0;
9448
- drawables && drawables.length > 0 ? drawables : childDrawablesRef.current;
9449
9558
  function isDrawableLayer(node) {
9450
9559
  return node.type === "draw-sprite-layer" || node.type === "draw-shape-layer" || node.type === "draw-text-layer";
9451
9560
  }
@@ -9594,11 +9703,24 @@ function Canvas2D({
9594
9703
  }, [showMinimap, scenePositions]);
9595
9704
  const miniMapWidth = gridExtent.width || 10;
9596
9705
  const miniMapHeight = gridExtent.height || 10;
9597
- const draw = React86.useCallback(() => {
9706
+ const drawableIsAnimated = (node) => {
9707
+ if (node.type === "draw-shape") return isAnimatedShape(node);
9708
+ if (node.type === "draw-group") return Array.isArray(node.items) && node.items.some(drawableIsAnimated);
9709
+ if (node.type === "draw-shape-layer") return Array.isArray(node.items) && node.items.some(isAnimatedShape);
9710
+ return false;
9711
+ };
9712
+ const animRafRef = React86.useRef(0);
9713
+ const drawTimeRef = React86.useRef(() => void 0);
9714
+ const draw = React86.useCallback((timeMs = 0) => {
9598
9715
  const canvas = canvasRef.current;
9599
9716
  if (!canvas) return;
9600
9717
  const ctx = canvas.getContext("2d");
9601
9718
  if (!ctx) return;
9719
+ const scheduleAnimation = (nodes) => {
9720
+ if (!nodes.some(drawableIsAnimated)) return;
9721
+ cancelAnimationFrame(animRafRef.current);
9722
+ animRafRef.current = requestAnimationFrame(() => drawTimeRef.current(performance.now()));
9723
+ };
9602
9724
  const dpr = window.devicePixelRatio || 1;
9603
9725
  canvas.width = viewportSize.width * dpr;
9604
9726
  canvas.height = viewportSize.height * dpr;
@@ -9631,14 +9753,24 @@ function Canvas2D({
9631
9753
  if (!drawables || drawables.length === 0) {
9632
9754
  const childDrawables = childDrawablesRef.current;
9633
9755
  if (childDrawables.length === 0) return;
9756
+ const cam0 = cameraRef.current;
9757
+ if (camera !== "follow" && dragDistance() === 0) {
9758
+ const focus = cameraPos ?? defaultGridFocus;
9759
+ if (focus) {
9760
+ const p = projector.anchorPoint(focus, "center");
9761
+ cam0.x = p.x - viewportSize.width / 2;
9762
+ cam0.y = p.y - viewportSize.height / 2;
9763
+ }
9764
+ }
9634
9765
  const painter0 = createWebPainter(ctx, bumpAtlas);
9635
9766
  painter0.save();
9636
9767
  painter0.translate(viewportSize.width / 2, viewportSize.height / 2);
9637
- painter0.scale(cameraRef.current.zoom, cameraRef.current.zoom);
9638
- painter0.translate(-viewportSize.width / 2, -viewportSize.height / 2);
9639
- const dctx0 = { projector, time: 0, invalidate: bumpAtlas };
9768
+ painter0.scale(cam0.zoom, cam0.zoom);
9769
+ painter0.translate(-viewportSize.width / 2 - cam0.x, -viewportSize.height / 2 - cam0.y);
9770
+ const dctx0 = { projector, time: timeMs, invalidate: bumpAtlas };
9640
9771
  for (const node of childDrawables) paintDrawable(painter0, node, dctx0);
9641
9772
  painter0.restore();
9773
+ scheduleAnimation(childDrawables);
9642
9774
  return;
9643
9775
  }
9644
9776
  const cam = cameraRef.current;
@@ -9658,10 +9790,15 @@ function Canvas2D({
9658
9790
  painter.translate(viewportSize.width / 2, viewportSize.height / 2);
9659
9791
  painter.scale(cam.zoom, cam.zoom);
9660
9792
  painter.translate(-viewportSize.width / 2 - cam.x, -viewportSize.height / 2 - cam.y);
9661
- const dctx = { projector, time: 0, invalidate: bumpAtlas };
9793
+ const dctx = { projector, time: timeMs, invalidate: bumpAtlas };
9662
9794
  for (const node of drawables) paintDrawable(painter, node, dctx);
9663
9795
  painter.restore();
9796
+ scheduleAnimation(drawables);
9664
9797
  }, [viewportSize, backgroundImage, bgColor, drawables, projector, cameraRef, bumpAtlas, getImage, cameraPos, defaultGridFocus, camera, dragDistance]);
9798
+ React86.useEffect(() => {
9799
+ drawTimeRef.current = draw;
9800
+ }, [draw]);
9801
+ React86.useEffect(() => () => cancelAnimationFrame(animRafRef.current), []);
9665
9802
  React86.useEffect(() => {
9666
9803
  if (camera !== "follow" || !followTarget) return;
9667
9804
  const p = projector.anchorPoint(followTarget, "center");
@@ -9884,15 +10021,7 @@ function Canvas2D({
9884
10021
  mapHeight: miniMapHeight
9885
10022
  }
9886
10023
  ) }),
9887
- hasJsxChildren && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children }),
9888
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-debug": "", style: { position: "absolute", top: 0, left: 0, background: "yellow", color: "black", zIndex: 9999, fontSize: 24, padding: 8 }, children: [
9889
- "C=",
9890
- React86__namespace.Children.count(children),
9891
- " J=",
9892
- String(hasJsxChildren),
9893
- " D=",
9894
- drawables?.length ?? -1
9895
- ] })
10024
+ hasJsxChildren && /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", style: { position: "absolute", width: 0, height: 0, overflow: "hidden" }, children })
9896
10025
  ]
9897
10026
  }
9898
10027
  ) });
@@ -9917,6 +10046,7 @@ var init_Canvas2D = __esm({
9917
10046
  init_webPainter2d();
9918
10047
  init_projector();
9919
10048
  init_paintDispatch();
10049
+ init_DrawShape();
9920
10050
  init_registry();
9921
10051
  init_hitTest();
9922
10052
  init_isometric();
@@ -45688,8 +45818,13 @@ function SlotContentRenderer({
45688
45818
  const isSingleChild = typeof childrenConfig === "string" || typeof childrenConfig === "object" && childrenConfig !== null && !Array.isArray(childrenConfig) && "type" in childrenConfig;
45689
45819
  const hasChildren = PATTERNS_WITH_CHILDREN.has(content.pattern) || Array.isArray(childrenConfig) && childrenConfig.length > 0 || isSingleChild;
45690
45820
  const isDrawHost = patterns.isDrawHostPattern(content.pattern);
45821
+ const arr = Array.isArray(childrenConfig) ? childrenConfig : childrenConfig ? [childrenConfig] : [];
45822
+ const hasTraitChildren = arr.some(
45823
+ (c) => typeof c === "string" && TRAIT_BINDING_RE.test(c)
45824
+ );
45825
+ const drawHostUsesReactChildren = isDrawHost && hasTraitChildren;
45691
45826
  const myPath = patternPath ?? "root";
45692
- const renderedChildren = hasChildren && !isDrawHost ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath, content.sourceTrait, {
45827
+ const renderedChildren = hasChildren && (!isDrawHost || drawHostUsesReactChildren) ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath, content.sourceTrait, {
45693
45828
  slot: content.slot,
45694
45829
  transitionEvent: content.transitionEvent,
45695
45830
  fromState: content.fromState,
@@ -45750,7 +45885,7 @@ function SlotContentRenderer({
45750
45885
  for (const [k, v] of Object.entries(nodeSlotOverrides)) {
45751
45886
  finalProps[k] = v;
45752
45887
  }
45753
- if (isDrawHost && Array.isArray(childrenConfig) && childrenConfig.length > 0) {
45888
+ if (isDrawHost && !drawHostUsesReactChildren && Array.isArray(childrenConfig) && childrenConfig.length > 0) {
45754
45889
  finalProps.drawables = toDrawableNodes(childrenConfig);
45755
45890
  }
45756
45891
  const entityVal = finalProps.entity;
@@ -45929,6 +46064,8 @@ var init_UISlotRenderer = __esm({
45929
46064
  "vstack",
45930
46065
  "hstack",
45931
46066
  "box",
46067
+ "canvas",
46068
+ "canvas-2d",
45932
46069
  "grid",
45933
46070
  "center",
45934
46071
  "card",