@almadar/ui 5.103.0 → 5.105.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.
Files changed (80) hide show
  1. package/dist/avl/index.cjs +168 -88
  2. package/dist/avl/index.css +3 -3
  3. package/dist/avl/index.js +168 -88
  4. package/dist/components/core/atoms/AtlasImage.d.ts +2 -2
  5. package/dist/components/core/atoms/FlipContainer.d.ts +1 -1
  6. package/dist/components/core/molecules/Card.d.ts +1 -1
  7. package/dist/components/core/molecules/Chart.d.ts +3 -3
  8. package/dist/components/core/molecules/DataGrid.d.ts +1 -1
  9. package/dist/components/core/molecules/DataList.d.ts +1 -1
  10. package/dist/components/core/molecules/DocPagination.d.ts +8 -1
  11. package/dist/components/core/molecules/DocSearch.d.ts +8 -1
  12. package/dist/components/core/molecules/FlipCard.d.ts +1 -1
  13. package/dist/components/core/molecules/LineChart.d.ts +3 -1
  14. package/dist/components/core/molecules/MapView.d.ts +18 -1
  15. package/dist/components/core/molecules/SortableList.d.ts +1 -1
  16. package/dist/components/core/molecules/TableView.d.ts +1 -1
  17. package/dist/components/core/organisms/DataTable.d.ts +7 -3
  18. package/dist/components/core/organisms/debug/RuntimeDebugger.d.ts +10 -1
  19. package/dist/components/core/organisms/debug/tabs/EntitiesTab.d.ts +2 -1
  20. package/dist/components/core/organisms/debug/tabs/EventDispatcherTab.d.ts +2 -1
  21. package/dist/components/core/organisms/debug/tabs/EventFlowTab.d.ts +6 -1
  22. package/dist/components/core/organisms/debug/tabs/GuardsPanel.d.ts +6 -1
  23. package/dist/components/core/organisms/debug/tabs/ServerBridgeTab.d.ts +12 -1
  24. package/dist/components/core/organisms/debug/tabs/TicksTab.d.ts +2 -1
  25. package/dist/components/core/organisms/debug/tabs/TraitsTab.d.ts +2 -1
  26. package/dist/components/core/organisms/debug/tabs/TransitionTimeline.d.ts +11 -1
  27. package/dist/components/core/organisms/debug/tabs/VerificationTab.d.ts +8 -1
  28. package/dist/components/game/2d/atoms/ActionButton.d.ts +2 -1
  29. package/dist/components/game/2d/atoms/ChoiceButton.d.ts +2 -1
  30. package/dist/components/game/2d/atoms/ComboCounter.d.ts +2 -1
  31. package/dist/components/game/2d/atoms/ControlButton.d.ts +2 -1
  32. package/dist/components/game/2d/atoms/DamageNumber.d.ts +2 -1
  33. package/dist/components/game/2d/atoms/DialogueBubble.d.ts +2 -1
  34. package/dist/components/game/2d/atoms/GameIcon.d.ts +2 -1
  35. package/dist/components/game/2d/atoms/HealthBar.d.ts +2 -1
  36. package/dist/components/game/2d/atoms/ItemSlot.d.ts +2 -1
  37. package/dist/components/game/2d/atoms/MiniMap.d.ts +2 -1
  38. package/dist/components/game/2d/atoms/ResourceCounter.d.ts +2 -1
  39. package/dist/components/game/2d/atoms/ScoreDisplay.d.ts +2 -1
  40. package/dist/components/game/2d/atoms/StatusEffect.d.ts +2 -1
  41. package/dist/components/game/2d/atoms/TimerDisplay.d.ts +2 -1
  42. package/dist/components/game/2d/atoms/TurnIndicator.d.ts +2 -1
  43. package/dist/components/game/2d/atoms/WaypointMarker.d.ts +2 -1
  44. package/dist/components/game/2d/molecules/Canvas2D.d.ts +6 -5
  45. package/dist/components/game/2d/molecules/GameHud.d.ts +2 -1
  46. package/dist/components/game/2d/molecules/GameMenu.d.ts +2 -1
  47. package/dist/components/game/2d/molecules/InventoryGrid.d.ts +2 -1
  48. package/dist/components/game/2d/molecules/ResourceBar.d.ts +2 -1
  49. package/dist/components/game/2d/molecules/SideCanvas2D.d.ts +36 -0
  50. package/dist/components/game/2d/molecules/StatBadge.d.ts +2 -1
  51. package/dist/components/game/3d/atoms/EventMarker3D.d.ts +17 -0
  52. package/dist/components/game/3d/atoms/SpriteBillboard3D.d.ts +18 -0
  53. package/dist/components/game/3d/index.cjs +539 -496
  54. package/dist/components/game/3d/index.js +367 -324
  55. package/dist/components/game/3d/molecules/FeatureMesh3D.d.ts +21 -0
  56. package/dist/components/game/3d/molecules/GameCamera3D.d.ts +35 -0
  57. package/dist/components/game/3d/molecules/Lighting3D.d.ts +10 -2
  58. package/dist/components/game/3d/molecules/SideScene3D.d.ts +31 -0
  59. package/dist/components/game/3d/molecules/TileMesh3D.d.ts +28 -0
  60. package/dist/components/game/3d/molecules/UnitMesh3D.d.ts +29 -0
  61. package/dist/components/game/shared/game3dTheme.d.ts +55 -0
  62. package/dist/components/game/shared/isometric.d.ts +38 -0
  63. package/dist/components/game/shared/isometricTypes.d.ts +4 -0
  64. package/dist/components/game/shared/lib/editorUtils.d.ts +8 -8
  65. package/dist/components/index.cjs +156 -83
  66. package/dist/components/index.css +3 -3
  67. package/dist/components/index.js +160 -87
  68. package/dist/hooks/useGitHub.d.ts +7 -7
  69. package/dist/marketing/index.cjs +2 -2
  70. package/dist/marketing/index.js +2 -2
  71. package/dist/providers/EventBusProvider.d.ts +1 -1
  72. package/dist/providers/SelectionProvider.d.ts +1 -1
  73. package/dist/providers/ServerBridge.d.ts +1 -1
  74. package/dist/providers/index.cjs +164 -84
  75. package/dist/providers/index.css +3 -3
  76. package/dist/providers/index.js +164 -84
  77. package/dist/runtime/index.cjs +164 -84
  78. package/dist/runtime/index.css +3 -3
  79. package/dist/runtime/index.js +164 -84
  80. package/package.json +10 -15
package/dist/avl/index.js CHANGED
@@ -584,7 +584,7 @@ var init_AvlFieldType = __esm({
584
584
  }
585
585
  });
586
586
 
587
- // node_modules/clsx/dist/clsx.mjs
587
+ // node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
588
588
  function r(e) {
589
589
  var t, f3, n = "";
590
590
  if ("string" == typeof e || "number" == typeof e) n += e;
@@ -599,11 +599,11 @@ function clsx() {
599
599
  return n;
600
600
  }
601
601
  var init_clsx = __esm({
602
- "node_modules/clsx/dist/clsx.mjs"() {
602
+ "node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs"() {
603
603
  }
604
604
  });
605
605
 
606
- // node_modules/tailwind-merge/dist/bundle-mjs.mjs
606
+ // node_modules/.pnpm/tailwind-merge@2.6.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs
607
607
  function twJoin() {
608
608
  let index = 0;
609
609
  let argument;
@@ -647,7 +647,7 @@ function createTailwindMerge(createConfigFirst, ...createConfigRest) {
647
647
  }
648
648
  var CLASS_PART_SEPARATOR, createClassGroupUtils, getGroupRecursive, arbitraryPropertyRegex, getGroupIdForArbitraryProperty, createClassMap, processClassesRecursively, getPart, isThemeGetter, getPrefixedClassGroupEntries, createLruCache, IMPORTANT_MODIFIER, createParseClassName, sortModifiers, createConfigUtils, SPLIT_CLASSES_REGEX, mergeClassList, toValue, fromTheme, arbitraryValueRegex, fractionRegex, stringLengths, tshirtUnitRegex, lengthUnitRegex, colorFunctionRegex, shadowRegex, imageRegex, isLength, isArbitraryLength, isNumber, isArbitraryNumber, isInteger, isPercent, isArbitraryValue, isTshirtSize, sizeLabels, isArbitrarySize, isArbitraryPosition, imageLabels, isArbitraryImage, isArbitraryShadow, isAny, getIsArbitraryValue, isLengthOnly, isNever, isShadow, isImage, getDefaultConfig, twMerge;
649
649
  var init_bundle_mjs = __esm({
650
- "node_modules/tailwind-merge/dist/bundle-mjs.mjs"() {
650
+ "node_modules/.pnpm/tailwind-merge@2.6.1/node_modules/tailwind-merge/dist/bundle-mjs.mjs"() {
651
651
  CLASS_PART_SEPARATOR = "-";
652
652
  createClassGroupUtils = (config) => {
653
653
  const classMap = createClassMap(config);
@@ -11203,7 +11203,7 @@ function screenToIso(screenX, screenY, scale, baseOffsetX, layout = "isometric")
11203
11203
  const tileY = (screenY / (scaledFloorHeight / 2) - adjustedX / (scaledTileWidth / 2)) / 2;
11204
11204
  return { x: Math.round(tileX), y: Math.round(tileY) };
11205
11205
  }
11206
- var TILE_WIDTH, TILE_HEIGHT, FLOOR_HEIGHT, DIAMOND_TOP_Y, FEATURE_COLORS;
11206
+ var TILE_WIDTH, TILE_HEIGHT, FLOOR_HEIGHT, DIAMOND_TOP_Y, FEATURE_COLORS, TERRAIN_COLORS, TEAM_COLORS, TEAM_SHADOW_COLORS, HIGHLIGHT_COLORS, SELECTION_RING_COLOR, GRID_STROKE_COLOR, BACKGROUND_FALLBACK_COLOR, MINIMAP_TERRAIN_COLORS, MINIMAP_UNIT_COLORS, UNIT_LABEL_BG_COLORS, SIDE_PLATFORM_COLORS, SIDE_PLAYER_COLOR, SIDE_PLAYER_EYE_COLOR, SIDE_SKY_GRADIENT_TOP, SIDE_SKY_GRADIENT_BOTTOM, SIDE_GRID_COLOR, SIDE_FX_COLOR, SIDE_PLATFORM_BEVEL_COLOR, SIDE_PLATFORM_SHADOW_COLOR, SIDE_HAZARD_STRIPE_COLOR, SIDE_GOAL_GLOW_COLOR, DEFAULT_BG_COLOR;
11207
11207
  var init_isometric = __esm({
11208
11208
  "components/game/shared/isometric.ts"() {
11209
11209
  TILE_WIDTH = 256;
@@ -11220,14 +11220,79 @@ var init_isometric = __esm({
11220
11220
  mountain: "#78716c",
11221
11221
  default: "#9ca3af"
11222
11222
  };
11223
+ TERRAIN_COLORS = {
11224
+ water: "#3b82f6",
11225
+ mountain: "#78716c",
11226
+ stone: "#9ca3af",
11227
+ default: "#4ade80"
11228
+ };
11229
+ TEAM_COLORS = {
11230
+ player: "#3b82f6",
11231
+ enemy: "#ef4444",
11232
+ neutral: "#6b7280",
11233
+ default: "#6b7280"
11234
+ };
11235
+ TEAM_SHADOW_COLORS = {
11236
+ player: "rgba(0,150,255,0.6)",
11237
+ default: "rgba(255,50,50,0.6)"
11238
+ };
11239
+ HIGHLIGHT_COLORS = {
11240
+ hover: "rgba(255,255,255,0.15)",
11241
+ validMove: "rgba(74,222,128,0.25)",
11242
+ attack: "rgba(239,68,68,0.35)"
11243
+ };
11244
+ SELECTION_RING_COLOR = "rgba(0,200,255,0.8)";
11245
+ GRID_STROKE_COLOR = "rgba(0,0,0,0.2)";
11246
+ BACKGROUND_FALLBACK_COLOR = "#1a1a2e";
11247
+ MINIMAP_TERRAIN_COLORS = {
11248
+ water: "#3b82f6",
11249
+ mountain: "#78716c",
11250
+ default: "#4ade80"
11251
+ };
11252
+ MINIMAP_UNIT_COLORS = {
11253
+ player: "#60a5fa",
11254
+ enemy: "#f87171",
11255
+ neutral: "#9ca3af",
11256
+ default: "#9ca3af"
11257
+ };
11258
+ UNIT_LABEL_BG_COLORS = {
11259
+ player: "rgba(59,130,246,0.9)",
11260
+ enemy: "rgba(239,68,68,0.9)",
11261
+ neutral: "rgba(107,114,128,0.9)",
11262
+ default: "rgba(107,114,128,0.9)"
11263
+ };
11264
+ SIDE_PLATFORM_COLORS = {
11265
+ ground: "#4a7c59",
11266
+ platform: "#7c6b4a",
11267
+ hazard: "#c0392b",
11268
+ goal: "#f1c40f"
11269
+ };
11270
+ SIDE_PLAYER_COLOR = "#3498db";
11271
+ SIDE_PLAYER_EYE_COLOR = "#ffffff";
11272
+ SIDE_SKY_GRADIENT_TOP = "#1a1a2e";
11273
+ SIDE_SKY_GRADIENT_BOTTOM = "#16213e";
11274
+ SIDE_GRID_COLOR = "rgba(255, 255, 255, 0.03)";
11275
+ SIDE_FX_COLOR = "#ffe066";
11276
+ SIDE_PLATFORM_BEVEL_COLOR = "rgba(255,255,255,0.15)";
11277
+ SIDE_PLATFORM_SHADOW_COLOR = "rgba(0,0,0,0.3)";
11278
+ SIDE_HAZARD_STRIPE_COLOR = "#e74c3c";
11279
+ SIDE_GOAL_GLOW_COLOR = "rgba(241, 196, 15, 0.5)";
11280
+ DEFAULT_BG_COLOR = "#5c94fc";
11223
11281
  }
11224
11282
  });
11225
11283
 
11226
11284
  // components/game/shared/spriteSheetConstants.ts
11227
- var SHEET_COLUMNS;
11285
+ var SHEET_COLUMNS, SPRITE_SHEET_LAYOUT;
11228
11286
  var init_spriteSheetConstants = __esm({
11229
11287
  "components/game/shared/spriteSheetConstants.ts"() {
11230
11288
  SHEET_COLUMNS = 8;
11289
+ SPRITE_SHEET_LAYOUT = {
11290
+ idle: { row: 0, frames: 4, frameRate: 6, loop: true },
11291
+ walk: { row: 1, frames: 8, frameRate: 10, loop: true },
11292
+ attack: { row: 2, frames: 6, frameRate: 12, loop: false },
11293
+ hit: { row: 3, frames: 3, frameRate: 8, loop: false },
11294
+ death: { row: 4, frames: 6, frameRate: 8, loop: false }
11295
+ };
11231
11296
  }
11232
11297
  });
11233
11298
 
@@ -13957,7 +14022,10 @@ var init_useUnitSpriteAtlas = __esm({
13957
14022
  init_spriteAnimation();
13958
14023
  }
13959
14024
  });
13960
- function SideView({
14025
+ function normalizeBackdrop(bg) {
14026
+ return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
14027
+ }
14028
+ function SideCanvas2D({
13961
14029
  player,
13962
14030
  platforms,
13963
14031
  worldWidth,
@@ -13966,7 +14034,7 @@ function SideView({
13966
14034
  canvasHeight,
13967
14035
  follow,
13968
14036
  bgColor,
13969
- backgroundImage,
14037
+ backgroundImage: backgroundImageRaw,
13970
14038
  playerSprite,
13971
14039
  tileSprites,
13972
14040
  effects,
@@ -13974,27 +14042,22 @@ function SideView({
13974
14042
  keyUpMap,
13975
14043
  className
13976
14044
  }) {
14045
+ const backgroundImage = normalizeBackdrop(backgroundImageRaw);
13977
14046
  const canvasRef = useRef(null);
13978
14047
  const eventBus = useEventBus();
13979
14048
  const keysRef = useRef(/* @__PURE__ */ new Set());
13980
- const imageCache2 = useRef(/* @__PURE__ */ new Map());
13981
- const [loadedImages, setLoadedImages] = useState(/* @__PURE__ */ new Set());
13982
- const loadImage = useCallback((url) => {
13983
- if (!url) return null;
13984
- const cached = imageCache2.current.get(url);
13985
- if (cached?.complete && cached.naturalWidth > 0) {
13986
- if (!loadedImages.has(url)) setLoadedImages((prev) => new Set(prev).add(url));
13987
- return cached;
13988
- }
13989
- if (!cached) {
13990
- const img = new Image();
13991
- img.crossOrigin = "anonymous";
13992
- img.src = url;
13993
- img.onload = () => setLoadedImages((prev) => new Set(prev).add(url));
13994
- imageCache2.current.set(url, img);
14049
+ const spriteUrls = useMemo(() => {
14050
+ const urls = [];
14051
+ if (backgroundImage?.url) urls.push(backgroundImage.url);
14052
+ if (playerSprite?.url) urls.push(playerSprite.url);
14053
+ if (tileSprites) {
14054
+ for (const asset of Object.values(tileSprites)) {
14055
+ if (asset?.url) urls.push(asset.url);
14056
+ }
13995
14057
  }
13996
- return null;
13997
- }, [loadedImages]);
14058
+ return urls;
14059
+ }, [backgroundImage, playerSprite, tileSprites]);
14060
+ const { getImage } = useImageCache(spriteUrls);
13998
14061
  useEffect(() => {
13999
14062
  if (typeof window === "undefined") return;
14000
14063
  const canvas = canvasRef.current;
@@ -14107,7 +14170,7 @@ function SideView({
14107
14170
  camX = Math.max(0, Math.min(px - cw / 2, ww - cw));
14108
14171
  camY = Math.max(0, Math.min(py - ch / 2 - 50, wh - ch));
14109
14172
  }
14110
- const bgImage = bgImg ? loadImage(bgImg.url) : null;
14173
+ const bgImage = bgImg ? getImage(bgImg.url) : void 0;
14111
14174
  const bgSrc = bgImage ? resolveAssetSource(bgImage, bgImg, NOOP) : null;
14112
14175
  if (bgSrc) {
14113
14176
  blit(ctx, bgSrc, 0, 0, cw, ch);
@@ -14118,12 +14181,12 @@ function SideView({
14118
14181
  ctx.fillRect(0, 0, cw, ch);
14119
14182
  } else {
14120
14183
  const grad = ctx.createLinearGradient(0, 0, 0, ch);
14121
- grad.addColorStop(0, SKY_GRADIENT_TOP);
14122
- grad.addColorStop(1, SKY_GRADIENT_BOTTOM);
14184
+ grad.addColorStop(0, SIDE_SKY_GRADIENT_TOP);
14185
+ grad.addColorStop(1, SIDE_SKY_GRADIENT_BOTTOM);
14123
14186
  ctx.fillStyle = grad;
14124
14187
  ctx.fillRect(0, 0, cw, ch);
14125
14188
  }
14126
- ctx.strokeStyle = GRID_COLOR;
14189
+ ctx.strokeStyle = SIDE_GRID_COLOR;
14127
14190
  ctx.lineWidth = 1;
14128
14191
  const gridSize = 32;
14129
14192
  for (let gx = -camX % gridSize; gx < cw; gx += gridSize) {
@@ -14143,7 +14206,7 @@ function SideView({
14143
14206
  const platY = plat.y - camY;
14144
14207
  const platType = plat.type ?? "ground";
14145
14208
  const spriteAsset = tSprites?.[platType];
14146
- const tileImg = spriteAsset ? loadImage(spriteAsset.url) : null;
14209
+ const tileImg = spriteAsset ? getImage(spriteAsset.url) : void 0;
14147
14210
  const tileSrc = tileImg ? resolveAssetSource(tileImg, spriteAsset, NOOP) : null;
14148
14211
  if (tileSrc) {
14149
14212
  const originX = tileSrc.rect ? tileSrc.rect.sx : 0;
@@ -14158,15 +14221,15 @@ function SideView({
14158
14221
  ctx.drawImage(tileImg, originX, originY, srcW, tileH, platX + tx, platY, drawW, plat.height);
14159
14222
  }
14160
14223
  } else {
14161
- const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
14224
+ const color = SIDE_PLATFORM_COLORS[platType] ?? SIDE_PLATFORM_COLORS.ground;
14162
14225
  ctx.fillStyle = color;
14163
14226
  ctx.fillRect(platX, platY, plat.width, plat.height);
14164
- ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
14227
+ ctx.fillStyle = SIDE_PLATFORM_BEVEL_COLOR;
14165
14228
  ctx.fillRect(platX, platY, plat.width, 3);
14166
- ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
14229
+ ctx.fillStyle = SIDE_PLATFORM_SHADOW_COLOR;
14167
14230
  ctx.fillRect(platX, platY + plat.height - 2, plat.width, 2);
14168
14231
  if (platType === "hazard") {
14169
- ctx.strokeStyle = "#e74c3c";
14232
+ ctx.strokeStyle = SIDE_HAZARD_STRIPE_COLOR;
14170
14233
  ctx.lineWidth = 2;
14171
14234
  for (let sx = platX; sx < platX + plat.width; sx += 12) {
14172
14235
  ctx.beginPath();
@@ -14176,7 +14239,7 @@ function SideView({
14176
14239
  }
14177
14240
  }
14178
14241
  if (platType === "goal") {
14179
- ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
14242
+ ctx.fillStyle = SIDE_GOAL_GLOW_COLOR;
14180
14243
  ctx.beginPath();
14181
14244
  ctx.arc(platX + plat.width / 2, platY - 10, 8, 0, Math.PI * 2);
14182
14245
  ctx.fill();
@@ -14188,7 +14251,7 @@ function SideView({
14188
14251
  const ppx = px - camX;
14189
14252
  const ppy = py - camY;
14190
14253
  const facingRight = auth.facingRight ?? true;
14191
- const playerImg = pSprite ? loadImage(pSprite.url) : null;
14254
+ const playerImg = pSprite ? getImage(pSprite.url) : void 0;
14192
14255
  const playerSrc = playerImg ? resolveAssetSource(playerImg, pSprite, NOOP) : null;
14193
14256
  if (playerSrc) {
14194
14257
  ctx.save();
@@ -14201,7 +14264,7 @@ function SideView({
14201
14264
  }
14202
14265
  ctx.restore();
14203
14266
  } else {
14204
- ctx.fillStyle = PLAYER_COLOR;
14267
+ ctx.fillStyle = SIDE_PLAYER_COLOR;
14205
14268
  const radius = Math.min(pw, ph) * 0.25;
14206
14269
  ctx.beginPath();
14207
14270
  ctx.moveTo(ppx + radius, ppy);
@@ -14217,7 +14280,7 @@ function SideView({
14217
14280
  const eyeY = ppy + ph * 0.3;
14218
14281
  const eyeSize = 3;
14219
14282
  const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
14220
- ctx.fillStyle = PLAYER_EYE_COLOR;
14283
+ ctx.fillStyle = SIDE_PLAYER_EYE_COLOR;
14221
14284
  ctx.beginPath();
14222
14285
  ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
14223
14286
  ctx.fill();
@@ -14231,7 +14294,7 @@ function SideView({
14231
14294
  const alpha = Math.min(1, fx.ttl / 4);
14232
14295
  const prev = ctx.globalAlpha;
14233
14296
  ctx.globalAlpha = alpha;
14234
- ctx.fillStyle = "#ffe066";
14297
+ ctx.fillStyle = SIDE_FX_COLOR;
14235
14298
  ctx.beginPath();
14236
14299
  ctx.arc(fxScreenX, fxScreenY, 12, 0, Math.PI * 2);
14237
14300
  ctx.fill();
@@ -14239,7 +14302,7 @@ function SideView({
14239
14302
  }
14240
14303
  };
14241
14304
  return interp.startLoop(drawFrame);
14242
- }, [interp.startLoop, loadImage]);
14305
+ }, [interp.startLoop, getImage]);
14243
14306
  return /* @__PURE__ */ jsx(
14244
14307
  "canvas",
14245
14308
  {
@@ -14251,6 +14314,33 @@ function SideView({
14251
14314
  }
14252
14315
  );
14253
14316
  }
14317
+ var NOOP;
14318
+ var init_SideCanvas2D = __esm({
14319
+ "components/game/2d/molecules/SideCanvas2D.tsx"() {
14320
+ "use client";
14321
+ init_cn();
14322
+ init_useEventBus();
14323
+ init_useImageCache();
14324
+ init_atlasSlice();
14325
+ init_useRenderInterpolation();
14326
+ init_verificationRegistry();
14327
+ init_isometric();
14328
+ NOOP = () => {
14329
+ };
14330
+ SideCanvas2D.displayName = "SideCanvas2D";
14331
+ }
14332
+ });
14333
+ function normalizeBackdrop2(bg) {
14334
+ return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
14335
+ }
14336
+ function resolveTileFallbackColor(tile) {
14337
+ const override = tile.color;
14338
+ return override ?? TERRAIN_COLORS[tile.terrain ?? ""] ?? TERRAIN_COLORS.default;
14339
+ }
14340
+ function resolveUnitTeamColor(unit) {
14341
+ const override = unit.tint;
14342
+ return override ?? TEAM_COLORS[unit.team ?? ""] ?? TEAM_COLORS.default;
14343
+ }
14254
14344
  function Canvas2D({
14255
14345
  // Closed-circuit
14256
14346
  className,
@@ -14265,7 +14355,7 @@ function Canvas2D({
14265
14355
  effects: _effectsPropRaw = [],
14266
14356
  platforms,
14267
14357
  player,
14268
- backgroundImage,
14358
+ backgroundImage: backgroundImageRaw,
14269
14359
  // Interaction state
14270
14360
  selectedUnitId = null,
14271
14361
  validMoves = [],
@@ -14300,12 +14390,13 @@ function Canvas2D({
14300
14390
  // Side-view asset resolution
14301
14391
  playerSprite,
14302
14392
  tileSprites,
14303
- bgColor = "#5c94fc",
14393
+ bgColor = DEFAULT_BG_COLOR,
14304
14394
  worldWidth = 800,
14305
14395
  worldHeight = 400,
14306
14396
  // Remote asset loading
14307
14397
  assetManifest
14308
14398
  }) {
14399
+ const backgroundImage = normalizeBackdrop2(backgroundImageRaw);
14309
14400
  const isSide = projection === "side";
14310
14401
  const isFree = projection === "free";
14311
14402
  const flatLike = projection === "hex" || projection === "flat" || isFree;
@@ -14489,7 +14580,7 @@ function Canvas2D({
14489
14580
  return sortedTiles.map((t2) => ({
14490
14581
  x: t2.x,
14491
14582
  y: t2.y,
14492
- color: t2.terrain === "water" ? "#3b82f6" : t2.terrain === "mountain" ? "#78716c" : "#4ade80"
14583
+ color: MINIMAP_TERRAIN_COLORS[t2.terrain ?? ""] ?? MINIMAP_TERRAIN_COLORS.default
14493
14584
  }));
14494
14585
  }, [showMinimap, sortedTiles]);
14495
14586
  const miniMapUnits = useMemo(() => {
@@ -14497,7 +14588,7 @@ function Canvas2D({
14497
14588
  return units.filter((u) => u.position).map((u) => ({
14498
14589
  x: u.position.x,
14499
14590
  y: u.position.y,
14500
- color: u.team === "player" ? "#60a5fa" : u.team === "enemy" ? "#f87171" : "#9ca3af",
14591
+ color: MINIMAP_UNIT_COLORS[u.team ?? ""] ?? MINIMAP_UNIT_COLORS.default,
14501
14592
  isPlayer: u.team === "player"
14502
14593
  }));
14503
14594
  }, [showMinimap, units]);
@@ -14534,7 +14625,7 @@ function Canvas2D({
14534
14625
  }
14535
14626
  }
14536
14627
  } else {
14537
- ctx.fillStyle = "#1a1a2e";
14628
+ ctx.fillStyle = BACKGROUND_FALLBACK_COLOR;
14538
14629
  ctx.fillRect(0, 0, viewportSize.width, viewportSize.height);
14539
14630
  }
14540
14631
  if (sortedTiles.length === 0 && units.length === 0 && features.length === 0) return;
@@ -14564,15 +14655,15 @@ function Canvas2D({
14564
14655
  } else if (img && img.naturalWidth === 0) {
14565
14656
  ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
14566
14657
  } else if (squareGrid) {
14567
- ctx.fillStyle = tile.terrain === "water" ? "#3b82f6" : tile.terrain === "mountain" ? "#78716c" : tile.terrain === "stone" ? "#9ca3af" : "#4ade80";
14658
+ ctx.fillStyle = resolveTileFallbackColor(tile);
14568
14659
  ctx.fillRect(pos.x, pos.y, scaledTileWidth, scaledTileWidth);
14569
- ctx.strokeStyle = "rgba(0,0,0,0.2)";
14660
+ ctx.strokeStyle = GRID_STROKE_COLOR;
14570
14661
  ctx.lineWidth = 1;
14571
14662
  ctx.strokeRect(pos.x, pos.y, scaledTileWidth, scaledTileWidth);
14572
14663
  } else {
14573
14664
  const centerX = pos.x + scaledTileWidth / 2;
14574
14665
  const topY = pos.y + scaledDiamondTopY;
14575
- ctx.fillStyle = tile.terrain === "water" ? "#3b82f6" : tile.terrain === "mountain" ? "#78716c" : tile.terrain === "stone" ? "#9ca3af" : "#4ade80";
14666
+ ctx.fillStyle = resolveTileFallbackColor(tile);
14576
14667
  ctx.beginPath();
14577
14668
  ctx.moveTo(centerX, topY);
14578
14669
  ctx.lineTo(pos.x + scaledTileWidth, topY + scaledFloorHeight / 2);
@@ -14580,7 +14671,7 @@ function Canvas2D({
14580
14671
  ctx.lineTo(pos.x, topY + scaledFloorHeight / 2);
14581
14672
  ctx.closePath();
14582
14673
  ctx.fill();
14583
- ctx.strokeStyle = "rgba(0,0,0,0.2)";
14674
+ ctx.strokeStyle = GRID_STROKE_COLOR;
14584
14675
  ctx.lineWidth = 1;
14585
14676
  ctx.stroke();
14586
14677
  }
@@ -14601,11 +14692,11 @@ function Canvas2D({
14601
14692
  ctx.fill();
14602
14693
  };
14603
14694
  if (hoveredTile && hoveredTile.x === tile.x && hoveredTile.y === tile.y) {
14604
- drawHighlight("rgba(255, 255, 255, 0.15)");
14695
+ drawHighlight(HIGHLIGHT_COLORS.hover);
14605
14696
  }
14606
14697
  const tileKey = `${tile.x},${tile.y}`;
14607
- if (validMoveSet.has(tileKey)) drawHighlight("rgba(74, 222, 128, 0.25)");
14608
- if (attackTargetSet.has(tileKey)) drawHighlight("rgba(239, 68, 68, 0.35)");
14698
+ if (validMoveSet.has(tileKey)) drawHighlight(HIGHLIGHT_COLORS.validMove);
14699
+ if (attackTargetSet.has(tileKey)) drawHighlight(HIGHLIGHT_COLORS.attack);
14609
14700
  if (debug2) {
14610
14701
  const centerX = pos.x + scaledTileWidth / 2;
14611
14702
  const centerY = squareGrid ? pos.y + scaledTileWidth / 2 : pos.y + scaledFloorHeight / 2 + scaledDiamondTopY;
@@ -14679,9 +14770,9 @@ function Canvas2D({
14679
14770
  const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
14680
14771
  const unitIsSheet = unit.spriteSheet?.url !== void 0;
14681
14772
  const unitSrc = img && !unitIsSheet ? resolveAssetSource(img, unitAsset, bumpAtlas) : null;
14682
- const SHEET_ROWS = 5;
14683
14773
  const sheetFrameW = img ? img.naturalWidth / SHEET_COLUMNS : 0;
14684
- const sheetFrameH = img ? img.naturalHeight / SHEET_ROWS : 0;
14774
+ const sheetFrameH = img ? img.naturalHeight / LEGACY_SHEET_ROWS : 0;
14775
+ const legacyFrame = frameRect(0, 0, SHEET_COLUMNS, sheetFrameW, sheetFrameH);
14685
14776
  const frameW = unitIsSheet ? sheetFrameW : img?.naturalWidth ?? 1;
14686
14777
  const frameH = unitIsSheet ? sheetFrameH : img?.naturalHeight ?? 1;
14687
14778
  const ar = unitIsSheet ? sheetFrameW / (sheetFrameH || 1) : unitSrc ? unitSrc.aspect : frameW / (frameH || 1);
@@ -14699,17 +14790,16 @@ function Canvas2D({
14699
14790
  ctx.globalAlpha = 0.25;
14700
14791
  if (img) {
14701
14792
  if (unitIsSheet) {
14702
- ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
14793
+ ctx.drawImage(img, legacyFrame.sx, legacyFrame.sy, legacyFrame.sw, legacyFrame.sh, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
14703
14794
  } else if (unitSrc) {
14704
14795
  blit(ctx, unitSrc, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
14705
14796
  } else {
14706
14797
  ctx.drawImage(img, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
14707
14798
  }
14708
14799
  } else {
14709
- const color = unit.team === "player" ? "#3b82f6" : unit.team === "enemy" ? "#ef4444" : "#6b7280";
14710
14800
  ctx.beginPath();
14711
14801
  ctx.arc(ghostCenterX, ghostGroundY - 16 * scale, 20 * scale, 0, Math.PI * 2);
14712
- ctx.fillStyle = color;
14802
+ ctx.fillStyle = resolveUnitTeamColor(unit);
14713
14803
  ctx.fill();
14714
14804
  }
14715
14805
  ctx.restore();
@@ -14717,7 +14807,7 @@ function Canvas2D({
14717
14807
  if (isSelected) {
14718
14808
  ctx.beginPath();
14719
14809
  ctx.ellipse(centerX, groundY, drawW / 2 + 4 * scale, 12 * scale, 0, 0, Math.PI * 2);
14720
- ctx.strokeStyle = "rgba(0, 200, 255, 0.8)";
14810
+ ctx.strokeStyle = SELECTION_RING_COLOR;
14721
14811
  ctx.lineWidth = 3;
14722
14812
  ctx.stroke();
14723
14813
  }
@@ -14734,7 +14824,7 @@ function Canvas2D({
14734
14824
  const spriteY = groundY - fDrawH - (frame.applyBreathing ? breatheOffset : 0);
14735
14825
  ctx.save();
14736
14826
  if (unit.team) {
14737
- ctx.shadowColor = unit.team === "player" ? "rgba(0, 150, 255, 0.6)" : "rgba(255, 50, 50, 0.6)";
14827
+ ctx.shadowColor = TEAM_SHADOW_COLORS[unit.team] ?? TEAM_SHADOW_COLORS.default;
14738
14828
  ctx.shadowBlur = 12 * scale;
14739
14829
  }
14740
14830
  if (frame.flipX) {
@@ -14749,7 +14839,7 @@ function Canvas2D({
14749
14839
  const spriteY = groundY - drawH - breatheOffset;
14750
14840
  const drawUnit = (x) => {
14751
14841
  if (unitIsSheet) {
14752
- ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, x, spriteY, drawW, drawH);
14842
+ ctx.drawImage(img, legacyFrame.sx, legacyFrame.sy, legacyFrame.sw, legacyFrame.sh, x, spriteY, drawW, drawH);
14753
14843
  } else if (unitSrc) {
14754
14844
  blit(ctx, unitSrc, x, spriteY, drawW, drawH);
14755
14845
  } else {
@@ -14758,7 +14848,7 @@ function Canvas2D({
14758
14848
  };
14759
14849
  if (unit.team) {
14760
14850
  ctx.save();
14761
- ctx.shadowColor = unit.team === "player" ? "rgba(0, 150, 255, 0.6)" : "rgba(255, 50, 50, 0.6)";
14851
+ ctx.shadowColor = TEAM_SHADOW_COLORS[unit.team] ?? TEAM_SHADOW_COLORS.default;
14762
14852
  ctx.shadowBlur = 12 * scale;
14763
14853
  drawUnit(centerX - drawW / 2);
14764
14854
  ctx.restore();
@@ -14766,10 +14856,9 @@ function Canvas2D({
14766
14856
  drawUnit(centerX - drawW / 2);
14767
14857
  }
14768
14858
  } else {
14769
- const color = unit.team === "player" ? "#3b82f6" : unit.team === "enemy" ? "#ef4444" : "#6b7280";
14770
14859
  ctx.beginPath();
14771
14860
  ctx.arc(centerX, groundY - 20 * scale - breatheOffset, 20 * scale, 0, Math.PI * 2);
14772
- ctx.fillStyle = color;
14861
+ ctx.fillStyle = resolveUnitTeamColor(unit);
14773
14862
  ctx.fill();
14774
14863
  ctx.strokeStyle = "rgba(255,255,255,0.8)";
14775
14864
  ctx.lineWidth = 2;
@@ -15008,7 +15097,7 @@ function Canvas2D({
15008
15097
  }
15009
15098
  if (isSide) {
15010
15099
  return /* @__PURE__ */ jsx(Box, { ref: containerRef, className: cn("relative overflow-hidden w-full h-full", className), children: /* @__PURE__ */ jsx(
15011
- SideView,
15100
+ SideCanvas2D,
15012
15101
  {
15013
15102
  player,
15014
15103
  platforms: platforms && platforms.length ? platforms : DEFAULT_PLATFORMS,
@@ -15106,7 +15195,7 @@ function Canvas2D({
15106
15195
  {
15107
15196
  as: "span",
15108
15197
  className: "text-white text-xs font-bold px-1.5 py-0.5 rounded mb-0.5 whitespace-nowrap block",
15109
- style: { background: unit.team === "player" ? "rgba(59,130,246,0.9)" : unit.team === "enemy" ? "rgba(239,68,68,0.9)" : "rgba(107,114,128,0.9)" },
15198
+ style: { background: UNIT_LABEL_BG_COLORS[unit.team ?? ""] ?? UNIT_LABEL_BG_COLORS.default },
15110
15199
  children: unit.name
15111
15200
  }
15112
15201
  ),
@@ -15130,7 +15219,7 @@ function Canvas2D({
15130
15219
  }
15131
15220
  );
15132
15221
  }
15133
- var PLATFORM_COLORS, NOOP, PLAYER_COLOR, PLAYER_EYE_COLOR, SKY_GRADIENT_TOP, SKY_GRADIENT_BOTTOM, GRID_COLOR, DEFAULT_PLATFORMS;
15222
+ var LEGACY_SHEET_ROWS, DEFAULT_PLATFORMS;
15134
15223
  var init_Canvas2D = __esm({
15135
15224
  "components/game/2d/molecules/Canvas2D.tsx"() {
15136
15225
  "use client";
@@ -15152,19 +15241,9 @@ var init_Canvas2D = __esm({
15152
15241
  init_verificationRegistry();
15153
15242
  init_isometric();
15154
15243
  init_spriteSheetConstants();
15155
- PLATFORM_COLORS = {
15156
- ground: "#4a7c59",
15157
- platform: "#7c6b4a",
15158
- hazard: "#c0392b",
15159
- goal: "#f1c40f"
15160
- };
15161
- NOOP = () => {
15162
- };
15163
- PLAYER_COLOR = "#3498db";
15164
- PLAYER_EYE_COLOR = "#ffffff";
15165
- SKY_GRADIENT_TOP = "#1a1a2e";
15166
- SKY_GRADIENT_BOTTOM = "#16213e";
15167
- GRID_COLOR = "rgba(255, 255, 255, 0.03)";
15244
+ init_spriteAnimation();
15245
+ init_SideCanvas2D();
15246
+ LEGACY_SHEET_ROWS = Object.keys(SPRITE_SHEET_LAYOUT).length;
15168
15247
  DEFAULT_PLATFORMS = [
15169
15248
  { x: 0, y: 368, width: 800, height: 32, type: "ground" },
15170
15249
  { x: 150, y: 280, width: 160, height: 16, type: "platform" },
@@ -20289,9 +20368,9 @@ var init_BiologyCanvas = __esm({
20289
20368
  }
20290
20369
  });
20291
20370
 
20292
- // node_modules/katex/dist/katex.min.css
20371
+ // node_modules/.pnpm/katex@0.16.47/node_modules/katex/dist/katex.min.css
20293
20372
  var init_katex_min = __esm({
20294
- "node_modules/katex/dist/katex.min.css"() {
20373
+ "node_modules/.pnpm/katex@0.16.47/node_modules/katex/dist/katex.min.css"() {
20295
20374
  }
20296
20375
  });
20297
20376
  var Tabs;
@@ -32497,8 +32576,9 @@ var init_LineChart = __esm({
32497
32576
  const safeData = data ?? [];
32498
32577
  const sortedData = useMemo(() => {
32499
32578
  if (safeData.length === 0) return [];
32579
+ if (!safeData.some((d) => d.date != null)) return [...safeData];
32500
32580
  return [...safeData].sort(
32501
- (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
32581
+ (a, b) => new Date(a.date ?? 0).getTime() - new Date(b.date ?? 0).getTime()
32502
32582
  );
32503
32583
  }, [safeData]);
32504
32584
  const points = useMemo(() => {
@@ -33249,13 +33329,13 @@ var init_MapView = __esm({
33249
33329
  shadowSize: [41, 41]
33250
33330
  });
33251
33331
  L.Marker.prototype.options.icon = defaultIcon;
33252
- const { useEffect: useEffect71, useRef: useRef68, useCallback: useCallback104, useState: useState105 } = React105__default;
33332
+ const { useEffect: useEffect72, useRef: useRef69, useCallback: useCallback105, useState: useState105 } = React105__default;
33253
33333
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
33254
33334
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
33255
33335
  function MapUpdater({ centerLat, centerLng, zoom }) {
33256
33336
  const map = useMap();
33257
- const prevRef = useRef68({ centerLat, centerLng, zoom });
33258
- useEffect71(() => {
33337
+ const prevRef = useRef69({ centerLat, centerLng, zoom });
33338
+ useEffect72(() => {
33259
33339
  const prev = prevRef.current;
33260
33340
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
33261
33341
  map.setView([centerLat, centerLng], zoom);
@@ -33266,7 +33346,7 @@ var init_MapView = __esm({
33266
33346
  }
33267
33347
  function MapClickHandler({ onMapClick }) {
33268
33348
  const map = useMap();
33269
- useEffect71(() => {
33349
+ useEffect72(() => {
33270
33350
  if (!onMapClick) return;
33271
33351
  const handler = (e) => {
33272
33352
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -33295,7 +33375,7 @@ var init_MapView = __esm({
33295
33375
  }) {
33296
33376
  const eventBus = useEventBus2();
33297
33377
  const [clickedPosition, setClickedPosition] = useState105(null);
33298
- const handleMapClick = useCallback104((lat, lng) => {
33378
+ const handleMapClick = useCallback105((lat, lng) => {
33299
33379
  if (showClickedPin) {
33300
33380
  setClickedPosition({ lat, lng });
33301
33381
  }
@@ -33304,7 +33384,7 @@ var init_MapView = __esm({
33304
33384
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
33305
33385
  }
33306
33386
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
33307
- const handleMarkerClick = useCallback104((marker) => {
33387
+ const handleMarkerClick = useCallback105((marker) => {
33308
33388
  onMarkerClick?.(marker);
33309
33389
  if (markerClickEvent) {
33310
33390
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -43,7 +43,7 @@ export interface AtlasPanelProps {
43
43
  * is cropped once to a dataURL; CSS border-image / background-repeat does the scaling. Falls back
44
44
  * to a plain container while the sheet/atlas load.
45
45
  */
46
- export declare function AtlasPanel({ asset, borderSlice, borderWidth, mode, className, style, children, 'aria-hidden': ariaHidden }: AtlasPanelProps): import("react/jsx-runtime").JSX.Element;
46
+ export declare function AtlasPanel({ asset, borderSlice, borderWidth, mode, className, style, children, 'aria-hidden': ariaHidden }: AtlasPanelProps): React.JSX.Element;
47
47
  export declare namespace AtlasPanel {
48
48
  var displayName: string;
49
49
  }
@@ -52,7 +52,7 @@ export declare namespace AtlasPanel {
52
52
  * sub-rect of the packed sheet into a canvas (never the whole sheet); otherwise it renders the
53
53
  * plain `<img>` path. `fill` + `fit` cover background/panel usage (GameShell, card frames).
54
54
  */
55
- export declare function AtlasImage({ asset, size, width, height, fill, fit, alt, className, style, 'aria-hidden': ariaHidden, }: AtlasImageProps): import("react/jsx-runtime").JSX.Element | null;
55
+ export declare function AtlasImage({ asset, size, width, height, fill, fit, alt, className, style, 'aria-hidden': ariaHidden, }: AtlasImageProps): React.JSX.Element | null;
56
56
  export declare namespace AtlasImage {
57
57
  var displayName: string;
58
58
  }
@@ -16,6 +16,6 @@ export interface FlipContainerProps {
16
16
  onClick?: () => void;
17
17
  }
18
18
  export declare const FlipContainer: {
19
- ({ flipped, className, children, onClick, }: FlipContainerProps): import("react/jsx-runtime").JSX.Element;
19
+ ({ flipped, className, children, onClick, }: FlipContainerProps): React.JSX.Element;
20
20
  displayName: string;
21
21
  };
@@ -51,7 +51,7 @@ export interface CardProps {
51
51
  /**
52
52
  * Card component for displaying content in a contained box
53
53
  */
54
- export declare function Card({ title, subtitle, image, actions, children, onClick, className, action, longPressEvent, longPressPayload, }: CardProps): import("react/jsx-runtime").JSX.Element;
54
+ export declare function Card({ title, subtitle, image, actions, children, onClick, className, action, longPressEvent, longPressPayload, }: CardProps): React.JSX.Element;
55
55
  export declare namespace Card {
56
56
  var displayName: string;
57
57
  }
@@ -35,7 +35,7 @@ export type ChartType = "bar" | "line" | "pie" | "area" | "donut" | "scatter" |
35
35
  */
36
36
  export type ChartLook = "bar-vertical" | "bar-horizontal" | "line" | "area" | "pie" | "donut" | "scatter" | "histogram";
37
37
  export type ChartStackMode = "none" | "stack" | "normalize";
38
- export interface ChartDataPoint {
38
+ export interface ChartSeriesPoint {
39
39
  label: string;
40
40
  value: number;
41
41
  color?: string;
@@ -49,7 +49,7 @@ export interface ChartScatterPoint {
49
49
  }
50
50
  export interface ChartSeries {
51
51
  name: string;
52
- data: readonly ChartDataPoint[];
52
+ data: readonly ChartSeriesPoint[];
53
53
  color?: string;
54
54
  dashed?: boolean;
55
55
  }
@@ -79,7 +79,7 @@ export interface ChartProps {
79
79
  /** Multi-series data */
80
80
  series?: readonly ChartSeries[];
81
81
  /** Simple single-series shorthand (bar/line/pie/area/donut/histogram) */
82
- data?: readonly ChartDataPoint[];
82
+ data?: readonly ChartSeriesPoint[];
83
83
  /** Raw {x,y} points for scatter */
84
84
  scatterData?: readonly ChartScatterPoint[];
85
85
  /** Chart height in px */