@almadar/ui 5.94.1 → 5.95.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.
@@ -10187,6 +10187,76 @@ var init_StateGraph = __esm({
10187
10187
  init_TransitionArrow();
10188
10188
  }
10189
10189
  });
10190
+
10191
+ // components/game/shared/atlasSlice.ts
10192
+ function isTilesheet(a) {
10193
+ return typeof a.tileWidth === "number";
10194
+ }
10195
+ function getAtlas(url, onReady) {
10196
+ if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
10197
+ atlasCache.set(url, void 0);
10198
+ fetch(url).then((r) => r.json()).then((json) => {
10199
+ atlasCache.set(url, json);
10200
+ onReady();
10201
+ }).catch(() => {
10202
+ atlasCache.set(url, null);
10203
+ });
10204
+ return void 0;
10205
+ }
10206
+ function subRectFor(atlas, sprite) {
10207
+ if (isTilesheet(atlas)) {
10208
+ let col;
10209
+ let row;
10210
+ if (sprite.includes(",")) {
10211
+ const [c, r] = sprite.split(",").map((n) => Number(n.trim()));
10212
+ col = c;
10213
+ row = r;
10214
+ } else {
10215
+ const i = Number(sprite);
10216
+ if (!Number.isFinite(i)) return null;
10217
+ col = i % atlas.columns;
10218
+ row = Math.floor(i / atlas.columns);
10219
+ }
10220
+ if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
10221
+ const margin = atlas.margin ?? 0;
10222
+ const spacing = atlas.spacing ?? 0;
10223
+ return {
10224
+ sx: margin + col * (atlas.tileWidth + spacing),
10225
+ sy: margin + row * (atlas.tileHeight + spacing),
10226
+ sw: atlas.tileWidth,
10227
+ sh: atlas.tileHeight
10228
+ };
10229
+ }
10230
+ const st = atlas.subTextures[sprite];
10231
+ if (!st) return null;
10232
+ return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
10233
+ }
10234
+ function isAtlasAsset(asset) {
10235
+ return !!asset && typeof asset.atlas === "string" && typeof asset.sprite === "string";
10236
+ }
10237
+ function resolveAssetSource(img, asset, onReady) {
10238
+ if (isAtlasAsset(asset)) {
10239
+ const atlas = getAtlas(asset.atlas, onReady);
10240
+ if (!atlas) return null;
10241
+ const rect = subRectFor(atlas, asset.sprite);
10242
+ if (!rect) return null;
10243
+ return { img, rect, aspect: rect.sw / rect.sh };
10244
+ }
10245
+ const natW = img.naturalWidth || 1;
10246
+ const natH = img.naturalHeight || 1;
10247
+ return { img, rect: null, aspect: natW / natH };
10248
+ }
10249
+ function blit(ctx, src, dx, dy, dw, dh) {
10250
+ if (src.rect) ctx.drawImage(src.img, src.rect.sx, src.rect.sy, src.rect.sw, src.rect.sh, dx, dy, dw, dh);
10251
+ else ctx.drawImage(src.img, dx, dy, dw, dh);
10252
+ }
10253
+ var atlasCache;
10254
+ var init_atlasSlice = __esm({
10255
+ "components/game/shared/atlasSlice.ts"() {
10256
+ "use client";
10257
+ atlasCache = /* @__PURE__ */ new Map();
10258
+ }
10259
+ });
10190
10260
  function useCamera() {
10191
10261
  const cameraRef = React95.useRef({ x: 0, y: 0, zoom: 1 });
10192
10262
  const targetCameraRef = React95.useRef(null);
@@ -10579,15 +10649,18 @@ function SideView({
10579
10649
  const platType = plat.type ?? "ground";
10580
10650
  const spriteAsset = tSprites?.[platType];
10581
10651
  const tileImg = spriteAsset ? loadImage(spriteAsset.url) : null;
10582
- if (tileImg) {
10583
- const tileW = tileImg.naturalWidth;
10584
- const tileH = tileImg.naturalHeight;
10652
+ const tileSrc = tileImg ? resolveAssetSource(tileImg, spriteAsset, NOOP) : null;
10653
+ if (tileSrc) {
10654
+ const originX = tileSrc.rect ? tileSrc.rect.sx : 0;
10655
+ const originY = tileSrc.rect ? tileSrc.rect.sy : 0;
10656
+ const tileW = tileSrc.rect ? tileSrc.rect.sw : tileImg.naturalWidth;
10657
+ const tileH = tileSrc.rect ? tileSrc.rect.sh : tileImg.naturalHeight;
10585
10658
  const scaleH = plat.height / tileH;
10586
10659
  const scaledW = tileW * scaleH;
10587
10660
  for (let tx = 0; tx < plat.width; tx += scaledW) {
10588
10661
  const drawW = Math.min(scaledW, plat.width - tx);
10589
10662
  const srcW = drawW / scaleH;
10590
- ctx.drawImage(tileImg, 0, 0, srcW, tileH, platX + tx, platY, drawW, plat.height);
10663
+ ctx.drawImage(tileImg, originX, originY, srcW, tileH, platX + tx, platY, drawW, plat.height);
10591
10664
  }
10592
10665
  } else {
10593
10666
  const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
@@ -10621,14 +10694,15 @@ function SideView({
10621
10694
  const ppy = py - camY;
10622
10695
  const facingRight = auth.facingRight ?? true;
10623
10696
  const playerImg = pSprite ? loadImage(pSprite.url) : null;
10624
- if (playerImg) {
10697
+ const playerSrc = playerImg ? resolveAssetSource(playerImg, pSprite, NOOP) : null;
10698
+ if (playerSrc) {
10625
10699
  ctx.save();
10626
10700
  if (!facingRight) {
10627
10701
  ctx.translate(ppx + pw, ppy);
10628
10702
  ctx.scale(-1, 1);
10629
- ctx.drawImage(playerImg, 0, 0, pw, ph);
10703
+ blit(ctx, playerSrc, 0, 0, pw, ph);
10630
10704
  } else {
10631
- ctx.drawImage(playerImg, ppx, ppy, pw, ph);
10705
+ blit(ctx, playerSrc, ppx, ppy, pw, ph);
10632
10706
  }
10633
10707
  ctx.restore();
10634
10708
  } else {
@@ -10829,10 +10903,11 @@ function Canvas2D({
10829
10903
  const attackTargetSet = React95.useMemo(() => new Set(attackTargets.map((p) => `${p.x},${p.y}`)), [attackTargets]);
10830
10904
  const spriteUrls = React95.useMemo(() => {
10831
10905
  const urls = [];
10906
+ const toUrl = (x) => typeof x === "string" ? x : x?.url;
10832
10907
  for (const tile of sortedTiles) {
10833
10908
  if (tile.terrainSprite) urls.push(tile.terrainSprite.url);
10834
10909
  else if (getTerrainSprite) {
10835
- const url = getTerrainSprite(tile.terrain ?? "");
10910
+ const url = toUrl(getTerrainSprite(tile.terrain ?? ""));
10836
10911
  if (url) urls.push(url);
10837
10912
  } else {
10838
10913
  const url = assetManifest?.terrains?.[tile.terrain ?? ""]?.url;
@@ -10842,7 +10917,7 @@ function Canvas2D({
10842
10917
  for (const feature of features) {
10843
10918
  if (feature.sprite) urls.push(feature.sprite.url);
10844
10919
  else if (getFeatureSprite) {
10845
- const url = getFeatureSprite(feature.type);
10920
+ const url = toUrl(getFeatureSprite(feature.type));
10846
10921
  if (url) urls.push(url);
10847
10922
  } else {
10848
10923
  const url = assetManifest?.features?.[feature.type]?.url;
@@ -10852,7 +10927,7 @@ function Canvas2D({
10852
10927
  for (const unit of units) {
10853
10928
  if (unit.sprite) urls.push(unit.sprite.url);
10854
10929
  else if (getUnitSprite) {
10855
- const url = getUnitSprite(unit);
10930
+ const url = toUrl(getUnitSprite(unit));
10856
10931
  if (url) urls.push(url);
10857
10932
  } else if (unit.unitType) {
10858
10933
  const url = assetManifest?.units?.[unit.unitType]?.url;
@@ -10893,14 +10968,25 @@ function Canvas2D({
10893
10968
  screenToWorld,
10894
10969
  lerpToTarget
10895
10970
  } = useCamera();
10896
- const resolveTerrainSpriteUrl = React95.useCallback((tile) => {
10897
- return tile.terrainSprite?.url || getTerrainSprite?.(tile.terrain ?? "") || assetManifest?.terrains?.[tile.terrain ?? ""]?.url;
10971
+ const [, setAtlasVersion] = React95.useState(0);
10972
+ const bumpAtlas = React95.useCallback(() => setAtlasVersion((v) => v + 1), []);
10973
+ const resolveTerrainAsset = React95.useCallback((tile) => {
10974
+ if (tile.terrainSprite) return tile.terrainSprite;
10975
+ const s = getTerrainSprite?.(tile.terrain ?? "");
10976
+ if (s) return typeof s === "string" ? { url: s } : s;
10977
+ return assetManifest?.terrains?.[tile.terrain ?? ""];
10898
10978
  }, [getTerrainSprite, assetManifest]);
10899
- const resolveFeatureSpriteUrl = React95.useCallback((featureType) => {
10900
- return getFeatureSprite?.(featureType) || assetManifest?.features?.[featureType]?.url;
10979
+ const resolveFeatureAsset = React95.useCallback((feature) => {
10980
+ if (feature.sprite) return feature.sprite;
10981
+ const s = getFeatureSprite?.(feature.type);
10982
+ if (s) return typeof s === "string" ? { url: s } : s;
10983
+ return assetManifest?.features?.[feature.type];
10901
10984
  }, [getFeatureSprite, assetManifest]);
10902
- const resolveUnitSpriteUrl = React95.useCallback((unit) => {
10903
- return unit.sprite?.url || getUnitSprite?.(unit) || (unit.unitType ? assetManifest?.units?.[unit.unitType]?.url : void 0);
10985
+ const resolveUnitAsset = React95.useCallback((unit) => {
10986
+ if (unit.sprite) return unit.sprite;
10987
+ const s = getUnitSprite?.(unit);
10988
+ if (s) return typeof s === "string" ? { url: s } : s;
10989
+ return unit.unitType ? assetManifest?.units?.[unit.unitType] : void 0;
10904
10990
  }, [getUnitSprite, assetManifest]);
10905
10991
  const miniMapTiles = React95.useMemo(() => {
10906
10992
  if (!showMinimap) return [];
@@ -10964,18 +11050,17 @@ function Canvas2D({
10964
11050
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
10965
11051
  continue;
10966
11052
  }
10967
- const spriteUrl = resolveTerrainSpriteUrl(tile);
10968
- const img = spriteUrl ? getImage(spriteUrl) : null;
10969
- if (img) {
10970
- if (img.naturalWidth === 0) {
10971
- ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
10972
- } else {
10973
- const drawW = scaledTileWidth;
10974
- const drawH = scaledTileWidth * (img.naturalHeight / img.naturalWidth);
10975
- const drawX = pos.x;
10976
- const drawY = flatLike ? pos.y : pos.y + scaledTileHeight - drawH;
10977
- ctx.drawImage(img, drawX, drawY, drawW, drawH);
10978
- }
11053
+ const terrainAsset = resolveTerrainAsset(tile);
11054
+ const img = terrainAsset?.url ? getImage(terrainAsset.url) : null;
11055
+ const src = img ? resolveAssetSource(img, terrainAsset, bumpAtlas) : null;
11056
+ if (src) {
11057
+ const drawW = scaledTileWidth;
11058
+ const drawH = scaledTileWidth / src.aspect;
11059
+ const drawX = pos.x;
11060
+ const drawY = flatLike ? pos.y : pos.y + scaledTileHeight - drawH;
11061
+ blit(ctx, src, drawX, drawY, drawW, drawH);
11062
+ } else if (img && img.naturalWidth === 0) {
11063
+ ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
10979
11064
  } else {
10980
11065
  const centerX = pos.x + scaledTileWidth / 2;
10981
11066
  const topY = pos.y + scaledDiamondTopY;
@@ -11029,15 +11114,16 @@ function Canvas2D({
11029
11114
  if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
11030
11115
  continue;
11031
11116
  }
11032
- const spriteUrl = feature.sprite?.url || resolveFeatureSpriteUrl(feature.type);
11033
- const img = spriteUrl ? getImage(spriteUrl) : null;
11117
+ const featureAsset = resolveFeatureAsset(feature);
11118
+ const img = featureAsset?.url ? getImage(featureAsset.url) : null;
11119
+ const src = img ? resolveAssetSource(img, featureAsset, bumpAtlas) : null;
11034
11120
  const centerX = pos.x + scaledTileWidth / 2;
11035
11121
  const featureGroundY = pos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
11036
11122
  const isCastle = feature.type === "castle";
11037
11123
  const featureDrawH = isCastle ? scaledFloorHeight * 3.5 : scaledFloorHeight * 1.6;
11038
11124
  const maxFeatureW = isCastle ? scaledTileWidth * 1.8 : scaledTileWidth * 0.7;
11039
- if (img) {
11040
- const ar = img.naturalWidth / img.naturalHeight;
11125
+ if (src) {
11126
+ const ar = src.aspect;
11041
11127
  let drawH = featureDrawH;
11042
11128
  let drawW = featureDrawH * ar;
11043
11129
  if (drawW > maxFeatureW) {
@@ -11046,7 +11132,7 @@ function Canvas2D({
11046
11132
  }
11047
11133
  const drawX = centerX - drawW / 2;
11048
11134
  const drawY = featureGroundY - drawH;
11049
- ctx.drawImage(img, drawX, drawY, drawW, drawH);
11135
+ blit(ctx, src, drawX, drawY, drawW, drawH);
11050
11136
  } else {
11051
11137
  const color = FEATURE_COLORS[feature.type] || FEATURE_COLORS.default;
11052
11138
  ctx.beginPath();
@@ -11075,17 +11161,18 @@ function Canvas2D({
11075
11161
  const centerX = pos.x + scaledTileWidth / 2;
11076
11162
  const groundY = pos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
11077
11163
  const breatheOffset = 0;
11078
- const unitSpriteUrl = resolveUnitSpriteUrl(unit);
11079
- const img = unitSpriteUrl ? getImage(unitSpriteUrl) : null;
11164
+ const unitAsset = resolveUnitAsset(unit);
11165
+ const img = unitAsset?.url ? getImage(unitAsset.url) : null;
11080
11166
  const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
11081
11167
  const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
11082
11168
  const unitIsSheet = unit.spriteSheet?.url !== void 0;
11169
+ const unitSrc = img && !unitIsSheet ? resolveAssetSource(img, unitAsset, bumpAtlas) : null;
11083
11170
  const SHEET_ROWS = 5;
11084
11171
  const sheetFrameW = img ? img.naturalWidth / SHEET_COLUMNS : 0;
11085
11172
  const sheetFrameH = img ? img.naturalHeight / SHEET_ROWS : 0;
11086
11173
  const frameW = unitIsSheet ? sheetFrameW : img?.naturalWidth ?? 1;
11087
11174
  const frameH = unitIsSheet ? sheetFrameH : img?.naturalHeight ?? 1;
11088
- const ar = frameW / (frameH || 1);
11175
+ const ar = unitIsSheet ? sheetFrameW / (sheetFrameH || 1) : unitSrc ? unitSrc.aspect : frameW / (frameH || 1);
11089
11176
  let drawH = unitDrawH;
11090
11177
  let drawW = unitDrawH * ar;
11091
11178
  if (drawW > maxUnitW) {
@@ -11101,6 +11188,8 @@ function Canvas2D({
11101
11188
  if (img) {
11102
11189
  if (unitIsSheet) {
11103
11190
  ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
11191
+ } else if (unitSrc) {
11192
+ blit(ctx, unitSrc, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
11104
11193
  } else {
11105
11194
  ctx.drawImage(img, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
11106
11195
  }
@@ -11149,6 +11238,8 @@ function Canvas2D({
11149
11238
  const drawUnit = (x) => {
11150
11239
  if (unitIsSheet) {
11151
11240
  ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, x, spriteY, drawW, drawH);
11241
+ } else if (unitSrc) {
11242
+ blit(ctx, unitSrc, x, spriteY, drawW, drawH);
11152
11243
  } else {
11153
11244
  ctx.drawImage(img, x, spriteY, drawW, drawH);
11154
11245
  }
@@ -11199,11 +11290,12 @@ function Canvas2D({
11199
11290
  flatLike,
11200
11291
  scale,
11201
11292
  debug2,
11202
- resolveTerrainSpriteUrl,
11203
- resolveFeatureSpriteUrl,
11204
- resolveUnitSpriteUrl,
11293
+ resolveTerrainAsset,
11294
+ resolveFeatureAsset,
11295
+ resolveUnitAsset,
11205
11296
  resolveFrameForUnit,
11206
11297
  getImage,
11298
+ bumpAtlas,
11207
11299
  baseOffsetX,
11208
11300
  scaledTileWidth,
11209
11301
  scaledTileHeight,
@@ -11517,7 +11609,7 @@ function Canvas2D({
11517
11609
  }
11518
11610
  );
11519
11611
  }
11520
- var PLATFORM_COLORS, PLAYER_COLOR, PLAYER_EYE_COLOR, SKY_GRADIENT_TOP, SKY_GRADIENT_BOTTOM, GRID_COLOR, DEFAULT_PLATFORMS;
11612
+ var PLATFORM_COLORS, NOOP, PLAYER_COLOR, PLAYER_EYE_COLOR, SKY_GRADIENT_TOP, SKY_GRADIENT_BOTTOM, GRID_COLOR, DEFAULT_PLATFORMS;
11521
11613
  var init_Canvas2D = __esm({
11522
11614
  "components/game/2d/molecules/Canvas2D.tsx"() {
11523
11615
  "use client";
@@ -11531,6 +11623,7 @@ var init_Canvas2D = __esm({
11531
11623
  init_MiniMap();
11532
11624
  init_HealthBar();
11533
11625
  init_useImageCache();
11626
+ init_atlasSlice();
11534
11627
  init_useCamera();
11535
11628
  init_useCanvasGestures();
11536
11629
  init_useRenderInterpolation();
@@ -11544,6 +11637,8 @@ var init_Canvas2D = __esm({
11544
11637
  hazard: "#c0392b",
11545
11638
  goal: "#f1c40f"
11546
11639
  };
11640
+ NOOP = () => {
11641
+ };
11547
11642
  PLAYER_COLOR = "#3498db";
11548
11643
  PLAYER_EYE_COLOR = "#ffffff";
11549
11644
  SKY_GRADIENT_TOP = "#1a1a2e";
@@ -16202,6 +16297,153 @@ var init_ComponentPatterns = __esm({
16202
16297
  AlertPattern.displayName = "AlertPattern";
16203
16298
  }
16204
16299
  });
16300
+ var DEFAULT_BAR_COLOR, DEFAULT_CELL_COLOR, DEFAULT_POINTER_COLOR, POINTER_BAND, TOP_PAD, AlgorithmCanvas;
16301
+ var init_AlgorithmCanvas = __esm({
16302
+ "components/learning/molecules/AlgorithmCanvas.tsx"() {
16303
+ "use client";
16304
+ init_atoms();
16305
+ init_Stack();
16306
+ init_LearningCanvas();
16307
+ DEFAULT_BAR_COLOR = "#3b82f6";
16308
+ DEFAULT_CELL_COLOR = "#e5e7eb";
16309
+ DEFAULT_POINTER_COLOR = "#dc2626";
16310
+ POINTER_BAND = 34;
16311
+ TOP_PAD = 12;
16312
+ AlgorithmCanvas = ({
16313
+ className,
16314
+ width = 600,
16315
+ height = 400,
16316
+ title,
16317
+ backgroundColor,
16318
+ bars = [],
16319
+ cells = [],
16320
+ pointers = [],
16321
+ shapes = [],
16322
+ interactive = false,
16323
+ animate = false,
16324
+ onShapeClick,
16325
+ isLoading,
16326
+ error
16327
+ }) => {
16328
+ const derivedShapes = React95.useMemo(() => {
16329
+ const out = [];
16330
+ if (bars.length > 0) {
16331
+ const slot = width / bars.length;
16332
+ const barW = slot * 0.8;
16333
+ const gap = slot * 0.1;
16334
+ const baseline = height - POINTER_BAND;
16335
+ const usableH = baseline - TOP_PAD;
16336
+ const maxV = Math.max(1, ...bars.map((b) => Number.isFinite(b.value) ? b.value : 0));
16337
+ bars.forEach((bar, i) => {
16338
+ const v = Number.isFinite(bar.value) ? bar.value : 0;
16339
+ const bh = Math.max(0, v / maxV * usableH);
16340
+ const x = i * slot + gap;
16341
+ const color = bar.color ?? DEFAULT_BAR_COLOR;
16342
+ out.push({
16343
+ type: "rect",
16344
+ id: `bar-${i}`,
16345
+ x,
16346
+ y: baseline - bh,
16347
+ width: barW,
16348
+ height: bh,
16349
+ color,
16350
+ fill: color
16351
+ });
16352
+ const label = bar.label ?? (bars.length <= 24 ? String(v) : void 0);
16353
+ if (label) {
16354
+ out.push({
16355
+ type: "text",
16356
+ x: x + barW / 2,
16357
+ y: baseline - bh - 8,
16358
+ text: label,
16359
+ color: "#374151",
16360
+ fontSize: 11,
16361
+ align: "center"
16362
+ });
16363
+ }
16364
+ });
16365
+ pointers.forEach((p) => {
16366
+ if (p.index < 0 || p.index >= bars.length) return;
16367
+ const cx = p.index * slot + slot / 2;
16368
+ const color = p.color ?? DEFAULT_POINTER_COLOR;
16369
+ out.push({
16370
+ type: "arrow",
16371
+ x1: cx,
16372
+ y1: height - 6,
16373
+ x2: cx,
16374
+ y2: baseline + 4,
16375
+ color,
16376
+ lineWidth: 2
16377
+ });
16378
+ if (p.label) {
16379
+ out.push({
16380
+ type: "text",
16381
+ x: cx,
16382
+ y: height - 22,
16383
+ text: p.label,
16384
+ color,
16385
+ fontSize: 11,
16386
+ align: "center"
16387
+ });
16388
+ }
16389
+ });
16390
+ }
16391
+ if (cells.length > 0) {
16392
+ const maxCol = Math.max(0, ...cells.map((c) => c.col)) + 1;
16393
+ const maxRow = Math.max(0, ...cells.map((c) => c.row)) + 1;
16394
+ const cw = width / maxCol;
16395
+ const ch = height / maxRow;
16396
+ cells.forEach((c, i) => {
16397
+ const x = c.col * cw;
16398
+ const y = c.row * ch;
16399
+ const color = c.color ?? DEFAULT_CELL_COLOR;
16400
+ out.push({
16401
+ type: "rect",
16402
+ id: `cell-${i}`,
16403
+ x: x + 1,
16404
+ y: y + 1,
16405
+ width: cw - 2,
16406
+ height: ch - 2,
16407
+ color: "#9ca3af",
16408
+ fill: color
16409
+ });
16410
+ const label = c.label ?? (c.value != null ? String(c.value) : void 0);
16411
+ if (label && cw >= 18 && ch >= 14) {
16412
+ out.push({
16413
+ type: "text",
16414
+ x: x + cw / 2,
16415
+ y: y + ch / 2,
16416
+ text: label,
16417
+ color: "#111827",
16418
+ fontSize: 12,
16419
+ align: "center"
16420
+ });
16421
+ }
16422
+ });
16423
+ }
16424
+ out.push(...shapes);
16425
+ return out;
16426
+ }, [bars, cells, pointers, shapes, width, height]);
16427
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { className, children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
16428
+ title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
16429
+ /* @__PURE__ */ jsxRuntime.jsx(
16430
+ LearningCanvas,
16431
+ {
16432
+ width,
16433
+ height,
16434
+ backgroundColor,
16435
+ shapes: derivedShapes,
16436
+ interactive,
16437
+ animate,
16438
+ onShapeClick,
16439
+ isLoading,
16440
+ error
16441
+ }
16442
+ )
16443
+ ] }) });
16444
+ };
16445
+ }
16446
+ });
16205
16447
  var AuthLayout;
16206
16448
  var init_AuthLayout = __esm({
16207
16449
  "components/marketing/templates/AuthLayout.tsx"() {
@@ -45560,6 +45802,7 @@ var init_component_registry_generated = __esm({
45560
45802
  init_ActionTile();
45561
45803
  init_ActivationBlock();
45562
45804
  init_ComponentPatterns();
45805
+ init_AlgorithmCanvas();
45563
45806
  init_AnimatedCounter();
45564
45807
  init_AnimatedGraphic();
45565
45808
  init_AnimatedReveal();
@@ -45858,6 +46101,7 @@ var init_component_registry_generated = __esm({
45858
46101
  "ActivationBlock": ActivationBlock,
45859
46102
  "Alert": AlertPattern,
45860
46103
  "AlertPattern": AlertPattern,
46104
+ "AlgorithmCanvas": AlgorithmCanvas,
45861
46105
  "AnimatedCounter": AnimatedCounter,
45862
46106
  "AnimatedGraphic": AnimatedGraphic,
45863
46107
  "AnimatedReveal": AnimatedReveal,