@almadar/ui 5.94.0 → 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.
- package/dist/avl/index.cjs +284 -40
- package/dist/avl/index.js +284 -40
- package/dist/components/core/molecules/index.d.ts +1 -0
- package/dist/components/game/2d/molecules/Canvas2D.d.ts +6 -6
- package/dist/components/game/shared/atlasSlice.d.ts +58 -0
- package/dist/components/index.cjs +594 -349
- package/dist/components/index.js +595 -350
- package/dist/components/learning/molecules/AlgorithmCanvas.d.ts +58 -0
- package/dist/providers/index.cjs +284 -40
- package/dist/providers/index.js +284 -40
- package/dist/runtime/index.cjs +284 -40
- package/dist/runtime/index.js +284 -40
- package/package.json +2 -2
package/dist/avl/index.js
CHANGED
|
@@ -13370,6 +13370,76 @@ var init_StateGraph = __esm({
|
|
|
13370
13370
|
init_TransitionArrow();
|
|
13371
13371
|
}
|
|
13372
13372
|
});
|
|
13373
|
+
|
|
13374
|
+
// components/game/shared/atlasSlice.ts
|
|
13375
|
+
function isTilesheet(a) {
|
|
13376
|
+
return typeof a.tileWidth === "number";
|
|
13377
|
+
}
|
|
13378
|
+
function getAtlas(url, onReady) {
|
|
13379
|
+
if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
|
|
13380
|
+
atlasCache.set(url, void 0);
|
|
13381
|
+
fetch(url).then((r2) => r2.json()).then((json) => {
|
|
13382
|
+
atlasCache.set(url, json);
|
|
13383
|
+
onReady();
|
|
13384
|
+
}).catch(() => {
|
|
13385
|
+
atlasCache.set(url, null);
|
|
13386
|
+
});
|
|
13387
|
+
return void 0;
|
|
13388
|
+
}
|
|
13389
|
+
function subRectFor(atlas, sprite) {
|
|
13390
|
+
if (isTilesheet(atlas)) {
|
|
13391
|
+
let col;
|
|
13392
|
+
let row;
|
|
13393
|
+
if (sprite.includes(",")) {
|
|
13394
|
+
const [c, r2] = sprite.split(",").map((n) => Number(n.trim()));
|
|
13395
|
+
col = c;
|
|
13396
|
+
row = r2;
|
|
13397
|
+
} else {
|
|
13398
|
+
const i = Number(sprite);
|
|
13399
|
+
if (!Number.isFinite(i)) return null;
|
|
13400
|
+
col = i % atlas.columns;
|
|
13401
|
+
row = Math.floor(i / atlas.columns);
|
|
13402
|
+
}
|
|
13403
|
+
if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
|
|
13404
|
+
const margin = atlas.margin ?? 0;
|
|
13405
|
+
const spacing = atlas.spacing ?? 0;
|
|
13406
|
+
return {
|
|
13407
|
+
sx: margin + col * (atlas.tileWidth + spacing),
|
|
13408
|
+
sy: margin + row * (atlas.tileHeight + spacing),
|
|
13409
|
+
sw: atlas.tileWidth,
|
|
13410
|
+
sh: atlas.tileHeight
|
|
13411
|
+
};
|
|
13412
|
+
}
|
|
13413
|
+
const st = atlas.subTextures[sprite];
|
|
13414
|
+
if (!st) return null;
|
|
13415
|
+
return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
|
|
13416
|
+
}
|
|
13417
|
+
function isAtlasAsset(asset) {
|
|
13418
|
+
return !!asset && typeof asset.atlas === "string" && typeof asset.sprite === "string";
|
|
13419
|
+
}
|
|
13420
|
+
function resolveAssetSource(img, asset, onReady) {
|
|
13421
|
+
if (isAtlasAsset(asset)) {
|
|
13422
|
+
const atlas = getAtlas(asset.atlas, onReady);
|
|
13423
|
+
if (!atlas) return null;
|
|
13424
|
+
const rect = subRectFor(atlas, asset.sprite);
|
|
13425
|
+
if (!rect) return null;
|
|
13426
|
+
return { img, rect, aspect: rect.sw / rect.sh };
|
|
13427
|
+
}
|
|
13428
|
+
const natW = img.naturalWidth || 1;
|
|
13429
|
+
const natH = img.naturalHeight || 1;
|
|
13430
|
+
return { img, rect: null, aspect: natW / natH };
|
|
13431
|
+
}
|
|
13432
|
+
function blit(ctx, src, dx, dy, dw, dh) {
|
|
13433
|
+
if (src.rect) ctx.drawImage(src.img, src.rect.sx, src.rect.sy, src.rect.sw, src.rect.sh, dx, dy, dw, dh);
|
|
13434
|
+
else ctx.drawImage(src.img, dx, dy, dw, dh);
|
|
13435
|
+
}
|
|
13436
|
+
var atlasCache;
|
|
13437
|
+
var init_atlasSlice = __esm({
|
|
13438
|
+
"components/game/shared/atlasSlice.ts"() {
|
|
13439
|
+
"use client";
|
|
13440
|
+
atlasCache = /* @__PURE__ */ new Map();
|
|
13441
|
+
}
|
|
13442
|
+
});
|
|
13373
13443
|
function useCamera() {
|
|
13374
13444
|
const cameraRef = useRef({ x: 0, y: 0, zoom: 1 });
|
|
13375
13445
|
const targetCameraRef = useRef(null);
|
|
@@ -13941,15 +14011,18 @@ function SideView({
|
|
|
13941
14011
|
const platType = plat.type ?? "ground";
|
|
13942
14012
|
const spriteAsset = tSprites?.[platType];
|
|
13943
14013
|
const tileImg = spriteAsset ? loadImage(spriteAsset.url) : null;
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
const
|
|
14014
|
+
const tileSrc = tileImg ? resolveAssetSource(tileImg, spriteAsset, NOOP) : null;
|
|
14015
|
+
if (tileSrc) {
|
|
14016
|
+
const originX = tileSrc.rect ? tileSrc.rect.sx : 0;
|
|
14017
|
+
const originY = tileSrc.rect ? tileSrc.rect.sy : 0;
|
|
14018
|
+
const tileW = tileSrc.rect ? tileSrc.rect.sw : tileImg.naturalWidth;
|
|
14019
|
+
const tileH = tileSrc.rect ? tileSrc.rect.sh : tileImg.naturalHeight;
|
|
13947
14020
|
const scaleH = plat.height / tileH;
|
|
13948
14021
|
const scaledW = tileW * scaleH;
|
|
13949
14022
|
for (let tx = 0; tx < plat.width; tx += scaledW) {
|
|
13950
14023
|
const drawW = Math.min(scaledW, plat.width - tx);
|
|
13951
14024
|
const srcW = drawW / scaleH;
|
|
13952
|
-
ctx.drawImage(tileImg,
|
|
14025
|
+
ctx.drawImage(tileImg, originX, originY, srcW, tileH, platX + tx, platY, drawW, plat.height);
|
|
13953
14026
|
}
|
|
13954
14027
|
} else {
|
|
13955
14028
|
const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
|
|
@@ -13983,14 +14056,15 @@ function SideView({
|
|
|
13983
14056
|
const ppy = py - camY;
|
|
13984
14057
|
const facingRight = auth.facingRight ?? true;
|
|
13985
14058
|
const playerImg = pSprite ? loadImage(pSprite.url) : null;
|
|
13986
|
-
|
|
14059
|
+
const playerSrc = playerImg ? resolveAssetSource(playerImg, pSprite, NOOP) : null;
|
|
14060
|
+
if (playerSrc) {
|
|
13987
14061
|
ctx.save();
|
|
13988
14062
|
if (!facingRight) {
|
|
13989
14063
|
ctx.translate(ppx + pw, ppy);
|
|
13990
14064
|
ctx.scale(-1, 1);
|
|
13991
|
-
ctx
|
|
14065
|
+
blit(ctx, playerSrc, 0, 0, pw, ph);
|
|
13992
14066
|
} else {
|
|
13993
|
-
ctx
|
|
14067
|
+
blit(ctx, playerSrc, ppx, ppy, pw, ph);
|
|
13994
14068
|
}
|
|
13995
14069
|
ctx.restore();
|
|
13996
14070
|
} else {
|
|
@@ -14191,10 +14265,11 @@ function Canvas2D({
|
|
|
14191
14265
|
const attackTargetSet = useMemo(() => new Set(attackTargets.map((p) => `${p.x},${p.y}`)), [attackTargets]);
|
|
14192
14266
|
const spriteUrls = useMemo(() => {
|
|
14193
14267
|
const urls = [];
|
|
14268
|
+
const toUrl = (x) => typeof x === "string" ? x : x?.url;
|
|
14194
14269
|
for (const tile of sortedTiles) {
|
|
14195
14270
|
if (tile.terrainSprite) urls.push(tile.terrainSprite.url);
|
|
14196
14271
|
else if (getTerrainSprite) {
|
|
14197
|
-
const url = getTerrainSprite(tile.terrain ?? "");
|
|
14272
|
+
const url = toUrl(getTerrainSprite(tile.terrain ?? ""));
|
|
14198
14273
|
if (url) urls.push(url);
|
|
14199
14274
|
} else {
|
|
14200
14275
|
const url = assetManifest?.terrains?.[tile.terrain ?? ""]?.url;
|
|
@@ -14204,7 +14279,7 @@ function Canvas2D({
|
|
|
14204
14279
|
for (const feature of features) {
|
|
14205
14280
|
if (feature.sprite) urls.push(feature.sprite.url);
|
|
14206
14281
|
else if (getFeatureSprite) {
|
|
14207
|
-
const url = getFeatureSprite(feature.type);
|
|
14282
|
+
const url = toUrl(getFeatureSprite(feature.type));
|
|
14208
14283
|
if (url) urls.push(url);
|
|
14209
14284
|
} else {
|
|
14210
14285
|
const url = assetManifest?.features?.[feature.type]?.url;
|
|
@@ -14214,7 +14289,7 @@ function Canvas2D({
|
|
|
14214
14289
|
for (const unit of units) {
|
|
14215
14290
|
if (unit.sprite) urls.push(unit.sprite.url);
|
|
14216
14291
|
else if (getUnitSprite) {
|
|
14217
|
-
const url = getUnitSprite(unit);
|
|
14292
|
+
const url = toUrl(getUnitSprite(unit));
|
|
14218
14293
|
if (url) urls.push(url);
|
|
14219
14294
|
} else if (unit.unitType) {
|
|
14220
14295
|
const url = assetManifest?.units?.[unit.unitType]?.url;
|
|
@@ -14255,14 +14330,25 @@ function Canvas2D({
|
|
|
14255
14330
|
screenToWorld,
|
|
14256
14331
|
lerpToTarget
|
|
14257
14332
|
} = useCamera();
|
|
14258
|
-
const
|
|
14259
|
-
|
|
14333
|
+
const [, setAtlasVersion] = useState(0);
|
|
14334
|
+
const bumpAtlas = useCallback(() => setAtlasVersion((v) => v + 1), []);
|
|
14335
|
+
const resolveTerrainAsset = useCallback((tile) => {
|
|
14336
|
+
if (tile.terrainSprite) return tile.terrainSprite;
|
|
14337
|
+
const s = getTerrainSprite?.(tile.terrain ?? "");
|
|
14338
|
+
if (s) return typeof s === "string" ? { url: s } : s;
|
|
14339
|
+
return assetManifest?.terrains?.[tile.terrain ?? ""];
|
|
14260
14340
|
}, [getTerrainSprite, assetManifest]);
|
|
14261
|
-
const
|
|
14262
|
-
|
|
14341
|
+
const resolveFeatureAsset = useCallback((feature) => {
|
|
14342
|
+
if (feature.sprite) return feature.sprite;
|
|
14343
|
+
const s = getFeatureSprite?.(feature.type);
|
|
14344
|
+
if (s) return typeof s === "string" ? { url: s } : s;
|
|
14345
|
+
return assetManifest?.features?.[feature.type];
|
|
14263
14346
|
}, [getFeatureSprite, assetManifest]);
|
|
14264
|
-
const
|
|
14265
|
-
|
|
14347
|
+
const resolveUnitAsset = useCallback((unit) => {
|
|
14348
|
+
if (unit.sprite) return unit.sprite;
|
|
14349
|
+
const s = getUnitSprite?.(unit);
|
|
14350
|
+
if (s) return typeof s === "string" ? { url: s } : s;
|
|
14351
|
+
return unit.unitType ? assetManifest?.units?.[unit.unitType] : void 0;
|
|
14266
14352
|
}, [getUnitSprite, assetManifest]);
|
|
14267
14353
|
const miniMapTiles = useMemo(() => {
|
|
14268
14354
|
if (!showMinimap) return [];
|
|
@@ -14326,18 +14412,17 @@ function Canvas2D({
|
|
|
14326
14412
|
if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
|
|
14327
14413
|
continue;
|
|
14328
14414
|
}
|
|
14329
|
-
const
|
|
14330
|
-
const img =
|
|
14331
|
-
|
|
14332
|
-
|
|
14333
|
-
|
|
14334
|
-
|
|
14335
|
-
|
|
14336
|
-
|
|
14337
|
-
|
|
14338
|
-
|
|
14339
|
-
|
|
14340
|
-
}
|
|
14415
|
+
const terrainAsset = resolveTerrainAsset(tile);
|
|
14416
|
+
const img = terrainAsset?.url ? getImage(terrainAsset.url) : null;
|
|
14417
|
+
const src = img ? resolveAssetSource(img, terrainAsset, bumpAtlas) : null;
|
|
14418
|
+
if (src) {
|
|
14419
|
+
const drawW = scaledTileWidth;
|
|
14420
|
+
const drawH = scaledTileWidth / src.aspect;
|
|
14421
|
+
const drawX = pos.x;
|
|
14422
|
+
const drawY = flatLike ? pos.y : pos.y + scaledTileHeight - drawH;
|
|
14423
|
+
blit(ctx, src, drawX, drawY, drawW, drawH);
|
|
14424
|
+
} else if (img && img.naturalWidth === 0) {
|
|
14425
|
+
ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
|
|
14341
14426
|
} else {
|
|
14342
14427
|
const centerX = pos.x + scaledTileWidth / 2;
|
|
14343
14428
|
const topY = pos.y + scaledDiamondTopY;
|
|
@@ -14391,15 +14476,16 @@ function Canvas2D({
|
|
|
14391
14476
|
if (pos.x + scaledTileWidth < visLeft || pos.x > visRight || pos.y + scaledTileHeight < visTop || pos.y > visBottom) {
|
|
14392
14477
|
continue;
|
|
14393
14478
|
}
|
|
14394
|
-
const
|
|
14395
|
-
const img =
|
|
14479
|
+
const featureAsset = resolveFeatureAsset(feature);
|
|
14480
|
+
const img = featureAsset?.url ? getImage(featureAsset.url) : null;
|
|
14481
|
+
const src = img ? resolveAssetSource(img, featureAsset, bumpAtlas) : null;
|
|
14396
14482
|
const centerX = pos.x + scaledTileWidth / 2;
|
|
14397
14483
|
const featureGroundY = pos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
|
|
14398
14484
|
const isCastle = feature.type === "castle";
|
|
14399
14485
|
const featureDrawH = isCastle ? scaledFloorHeight * 3.5 : scaledFloorHeight * 1.6;
|
|
14400
14486
|
const maxFeatureW = isCastle ? scaledTileWidth * 1.8 : scaledTileWidth * 0.7;
|
|
14401
|
-
if (
|
|
14402
|
-
const ar =
|
|
14487
|
+
if (src) {
|
|
14488
|
+
const ar = src.aspect;
|
|
14403
14489
|
let drawH = featureDrawH;
|
|
14404
14490
|
let drawW = featureDrawH * ar;
|
|
14405
14491
|
if (drawW > maxFeatureW) {
|
|
@@ -14408,7 +14494,7 @@ function Canvas2D({
|
|
|
14408
14494
|
}
|
|
14409
14495
|
const drawX = centerX - drawW / 2;
|
|
14410
14496
|
const drawY = featureGroundY - drawH;
|
|
14411
|
-
ctx
|
|
14497
|
+
blit(ctx, src, drawX, drawY, drawW, drawH);
|
|
14412
14498
|
} else {
|
|
14413
14499
|
const color = FEATURE_COLORS[feature.type] || FEATURE_COLORS.default;
|
|
14414
14500
|
ctx.beginPath();
|
|
@@ -14437,17 +14523,18 @@ function Canvas2D({
|
|
|
14437
14523
|
const centerX = pos.x + scaledTileWidth / 2;
|
|
14438
14524
|
const groundY = pos.y + scaledDiamondTopY + scaledFloorHeight * 0.5;
|
|
14439
14525
|
const breatheOffset = 0;
|
|
14440
|
-
const
|
|
14441
|
-
const img =
|
|
14526
|
+
const unitAsset = resolveUnitAsset(unit);
|
|
14527
|
+
const img = unitAsset?.url ? getImage(unitAsset.url) : null;
|
|
14442
14528
|
const unitDrawH = scaledFloorHeight * spriteHeightRatio * unitScale;
|
|
14443
14529
|
const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
|
|
14444
14530
|
const unitIsSheet = unit.spriteSheet?.url !== void 0;
|
|
14531
|
+
const unitSrc = img && !unitIsSheet ? resolveAssetSource(img, unitAsset, bumpAtlas) : null;
|
|
14445
14532
|
const SHEET_ROWS = 5;
|
|
14446
14533
|
const sheetFrameW = img ? img.naturalWidth / SHEET_COLUMNS : 0;
|
|
14447
14534
|
const sheetFrameH = img ? img.naturalHeight / SHEET_ROWS : 0;
|
|
14448
14535
|
const frameW = unitIsSheet ? sheetFrameW : img?.naturalWidth ?? 1;
|
|
14449
14536
|
const frameH = unitIsSheet ? sheetFrameH : img?.naturalHeight ?? 1;
|
|
14450
|
-
const ar = frameW / (frameH || 1);
|
|
14537
|
+
const ar = unitIsSheet ? sheetFrameW / (sheetFrameH || 1) : unitSrc ? unitSrc.aspect : frameW / (frameH || 1);
|
|
14451
14538
|
let drawH = unitDrawH;
|
|
14452
14539
|
let drawW = unitDrawH * ar;
|
|
14453
14540
|
if (drawW > maxUnitW) {
|
|
@@ -14463,6 +14550,8 @@ function Canvas2D({
|
|
|
14463
14550
|
if (img) {
|
|
14464
14551
|
if (unitIsSheet) {
|
|
14465
14552
|
ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
|
|
14553
|
+
} else if (unitSrc) {
|
|
14554
|
+
blit(ctx, unitSrc, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
|
|
14466
14555
|
} else {
|
|
14467
14556
|
ctx.drawImage(img, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
|
|
14468
14557
|
}
|
|
@@ -14511,6 +14600,8 @@ function Canvas2D({
|
|
|
14511
14600
|
const drawUnit = (x) => {
|
|
14512
14601
|
if (unitIsSheet) {
|
|
14513
14602
|
ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, x, spriteY, drawW, drawH);
|
|
14603
|
+
} else if (unitSrc) {
|
|
14604
|
+
blit(ctx, unitSrc, x, spriteY, drawW, drawH);
|
|
14514
14605
|
} else {
|
|
14515
14606
|
ctx.drawImage(img, x, spriteY, drawW, drawH);
|
|
14516
14607
|
}
|
|
@@ -14561,11 +14652,12 @@ function Canvas2D({
|
|
|
14561
14652
|
flatLike,
|
|
14562
14653
|
scale,
|
|
14563
14654
|
debug2,
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14655
|
+
resolveTerrainAsset,
|
|
14656
|
+
resolveFeatureAsset,
|
|
14657
|
+
resolveUnitAsset,
|
|
14567
14658
|
resolveFrameForUnit,
|
|
14568
14659
|
getImage,
|
|
14660
|
+
bumpAtlas,
|
|
14569
14661
|
baseOffsetX,
|
|
14570
14662
|
scaledTileWidth,
|
|
14571
14663
|
scaledTileHeight,
|
|
@@ -14879,7 +14971,7 @@ function Canvas2D({
|
|
|
14879
14971
|
}
|
|
14880
14972
|
);
|
|
14881
14973
|
}
|
|
14882
|
-
var PLATFORM_COLORS, PLAYER_COLOR, PLAYER_EYE_COLOR, SKY_GRADIENT_TOP, SKY_GRADIENT_BOTTOM, GRID_COLOR, DEFAULT_PLATFORMS;
|
|
14974
|
+
var PLATFORM_COLORS, NOOP, PLAYER_COLOR, PLAYER_EYE_COLOR, SKY_GRADIENT_TOP, SKY_GRADIENT_BOTTOM, GRID_COLOR, DEFAULT_PLATFORMS;
|
|
14883
14975
|
var init_Canvas2D = __esm({
|
|
14884
14976
|
"components/game/2d/molecules/Canvas2D.tsx"() {
|
|
14885
14977
|
"use client";
|
|
@@ -14893,6 +14985,7 @@ var init_Canvas2D = __esm({
|
|
|
14893
14985
|
init_MiniMap();
|
|
14894
14986
|
init_HealthBar();
|
|
14895
14987
|
init_useImageCache();
|
|
14988
|
+
init_atlasSlice();
|
|
14896
14989
|
init_useCamera();
|
|
14897
14990
|
init_useCanvasGestures();
|
|
14898
14991
|
init_useRenderInterpolation();
|
|
@@ -14906,6 +14999,8 @@ var init_Canvas2D = __esm({
|
|
|
14906
14999
|
hazard: "#c0392b",
|
|
14907
15000
|
goal: "#f1c40f"
|
|
14908
15001
|
};
|
|
15002
|
+
NOOP = () => {
|
|
15003
|
+
};
|
|
14909
15004
|
PLAYER_COLOR = "#3498db";
|
|
14910
15005
|
PLAYER_EYE_COLOR = "#ffffff";
|
|
14911
15006
|
SKY_GRADIENT_TOP = "#1a1a2e";
|
|
@@ -19564,6 +19659,153 @@ var init_ComponentPatterns = __esm({
|
|
|
19564
19659
|
AlertPattern.displayName = "AlertPattern";
|
|
19565
19660
|
}
|
|
19566
19661
|
});
|
|
19662
|
+
var DEFAULT_BAR_COLOR, DEFAULT_CELL_COLOR, DEFAULT_POINTER_COLOR, POINTER_BAND, TOP_PAD, AlgorithmCanvas;
|
|
19663
|
+
var init_AlgorithmCanvas = __esm({
|
|
19664
|
+
"components/learning/molecules/AlgorithmCanvas.tsx"() {
|
|
19665
|
+
"use client";
|
|
19666
|
+
init_atoms();
|
|
19667
|
+
init_Stack();
|
|
19668
|
+
init_LearningCanvas();
|
|
19669
|
+
DEFAULT_BAR_COLOR = "#3b82f6";
|
|
19670
|
+
DEFAULT_CELL_COLOR = "#e5e7eb";
|
|
19671
|
+
DEFAULT_POINTER_COLOR = "#dc2626";
|
|
19672
|
+
POINTER_BAND = 34;
|
|
19673
|
+
TOP_PAD = 12;
|
|
19674
|
+
AlgorithmCanvas = ({
|
|
19675
|
+
className,
|
|
19676
|
+
width = 600,
|
|
19677
|
+
height = 400,
|
|
19678
|
+
title,
|
|
19679
|
+
backgroundColor,
|
|
19680
|
+
bars = [],
|
|
19681
|
+
cells = [],
|
|
19682
|
+
pointers = [],
|
|
19683
|
+
shapes = [],
|
|
19684
|
+
interactive = false,
|
|
19685
|
+
animate = false,
|
|
19686
|
+
onShapeClick,
|
|
19687
|
+
isLoading,
|
|
19688
|
+
error
|
|
19689
|
+
}) => {
|
|
19690
|
+
const derivedShapes = useMemo(() => {
|
|
19691
|
+
const out = [];
|
|
19692
|
+
if (bars.length > 0) {
|
|
19693
|
+
const slot = width / bars.length;
|
|
19694
|
+
const barW = slot * 0.8;
|
|
19695
|
+
const gap = slot * 0.1;
|
|
19696
|
+
const baseline = height - POINTER_BAND;
|
|
19697
|
+
const usableH = baseline - TOP_PAD;
|
|
19698
|
+
const maxV = Math.max(1, ...bars.map((b) => Number.isFinite(b.value) ? b.value : 0));
|
|
19699
|
+
bars.forEach((bar, i) => {
|
|
19700
|
+
const v = Number.isFinite(bar.value) ? bar.value : 0;
|
|
19701
|
+
const bh = Math.max(0, v / maxV * usableH);
|
|
19702
|
+
const x = i * slot + gap;
|
|
19703
|
+
const color = bar.color ?? DEFAULT_BAR_COLOR;
|
|
19704
|
+
out.push({
|
|
19705
|
+
type: "rect",
|
|
19706
|
+
id: `bar-${i}`,
|
|
19707
|
+
x,
|
|
19708
|
+
y: baseline - bh,
|
|
19709
|
+
width: barW,
|
|
19710
|
+
height: bh,
|
|
19711
|
+
color,
|
|
19712
|
+
fill: color
|
|
19713
|
+
});
|
|
19714
|
+
const label = bar.label ?? (bars.length <= 24 ? String(v) : void 0);
|
|
19715
|
+
if (label) {
|
|
19716
|
+
out.push({
|
|
19717
|
+
type: "text",
|
|
19718
|
+
x: x + barW / 2,
|
|
19719
|
+
y: baseline - bh - 8,
|
|
19720
|
+
text: label,
|
|
19721
|
+
color: "#374151",
|
|
19722
|
+
fontSize: 11,
|
|
19723
|
+
align: "center"
|
|
19724
|
+
});
|
|
19725
|
+
}
|
|
19726
|
+
});
|
|
19727
|
+
pointers.forEach((p) => {
|
|
19728
|
+
if (p.index < 0 || p.index >= bars.length) return;
|
|
19729
|
+
const cx = p.index * slot + slot / 2;
|
|
19730
|
+
const color = p.color ?? DEFAULT_POINTER_COLOR;
|
|
19731
|
+
out.push({
|
|
19732
|
+
type: "arrow",
|
|
19733
|
+
x1: cx,
|
|
19734
|
+
y1: height - 6,
|
|
19735
|
+
x2: cx,
|
|
19736
|
+
y2: baseline + 4,
|
|
19737
|
+
color,
|
|
19738
|
+
lineWidth: 2
|
|
19739
|
+
});
|
|
19740
|
+
if (p.label) {
|
|
19741
|
+
out.push({
|
|
19742
|
+
type: "text",
|
|
19743
|
+
x: cx,
|
|
19744
|
+
y: height - 22,
|
|
19745
|
+
text: p.label,
|
|
19746
|
+
color,
|
|
19747
|
+
fontSize: 11,
|
|
19748
|
+
align: "center"
|
|
19749
|
+
});
|
|
19750
|
+
}
|
|
19751
|
+
});
|
|
19752
|
+
}
|
|
19753
|
+
if (cells.length > 0) {
|
|
19754
|
+
const maxCol = Math.max(0, ...cells.map((c) => c.col)) + 1;
|
|
19755
|
+
const maxRow = Math.max(0, ...cells.map((c) => c.row)) + 1;
|
|
19756
|
+
const cw = width / maxCol;
|
|
19757
|
+
const ch = height / maxRow;
|
|
19758
|
+
cells.forEach((c, i) => {
|
|
19759
|
+
const x = c.col * cw;
|
|
19760
|
+
const y = c.row * ch;
|
|
19761
|
+
const color = c.color ?? DEFAULT_CELL_COLOR;
|
|
19762
|
+
out.push({
|
|
19763
|
+
type: "rect",
|
|
19764
|
+
id: `cell-${i}`,
|
|
19765
|
+
x: x + 1,
|
|
19766
|
+
y: y + 1,
|
|
19767
|
+
width: cw - 2,
|
|
19768
|
+
height: ch - 2,
|
|
19769
|
+
color: "#9ca3af",
|
|
19770
|
+
fill: color
|
|
19771
|
+
});
|
|
19772
|
+
const label = c.label ?? (c.value != null ? String(c.value) : void 0);
|
|
19773
|
+
if (label && cw >= 18 && ch >= 14) {
|
|
19774
|
+
out.push({
|
|
19775
|
+
type: "text",
|
|
19776
|
+
x: x + cw / 2,
|
|
19777
|
+
y: y + ch / 2,
|
|
19778
|
+
text: label,
|
|
19779
|
+
color: "#111827",
|
|
19780
|
+
fontSize: 12,
|
|
19781
|
+
align: "center"
|
|
19782
|
+
});
|
|
19783
|
+
}
|
|
19784
|
+
});
|
|
19785
|
+
}
|
|
19786
|
+
out.push(...shapes);
|
|
19787
|
+
return out;
|
|
19788
|
+
}, [bars, cells, pointers, shapes, width, height]);
|
|
19789
|
+
return /* @__PURE__ */ jsx(Card, { className, children: /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
19790
|
+
title ? /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title }) : null,
|
|
19791
|
+
/* @__PURE__ */ jsx(
|
|
19792
|
+
LearningCanvas,
|
|
19793
|
+
{
|
|
19794
|
+
width,
|
|
19795
|
+
height,
|
|
19796
|
+
backgroundColor,
|
|
19797
|
+
shapes: derivedShapes,
|
|
19798
|
+
interactive,
|
|
19799
|
+
animate,
|
|
19800
|
+
onShapeClick,
|
|
19801
|
+
isLoading,
|
|
19802
|
+
error
|
|
19803
|
+
}
|
|
19804
|
+
)
|
|
19805
|
+
] }) });
|
|
19806
|
+
};
|
|
19807
|
+
}
|
|
19808
|
+
});
|
|
19567
19809
|
var AuthLayout;
|
|
19568
19810
|
var init_AuthLayout = __esm({
|
|
19569
19811
|
"components/marketing/templates/AuthLayout.tsx"() {
|
|
@@ -48019,6 +48261,7 @@ var init_component_registry_generated = __esm({
|
|
|
48019
48261
|
init_ActionTile();
|
|
48020
48262
|
init_ActivationBlock();
|
|
48021
48263
|
init_ComponentPatterns();
|
|
48264
|
+
init_AlgorithmCanvas();
|
|
48022
48265
|
init_AnimatedCounter();
|
|
48023
48266
|
init_AnimatedGraphic();
|
|
48024
48267
|
init_AnimatedReveal();
|
|
@@ -48317,6 +48560,7 @@ var init_component_registry_generated = __esm({
|
|
|
48317
48560
|
"ActivationBlock": ActivationBlock,
|
|
48318
48561
|
"Alert": AlertPattern,
|
|
48319
48562
|
"AlertPattern": AlertPattern,
|
|
48563
|
+
"AlgorithmCanvas": AlgorithmCanvas,
|
|
48320
48564
|
"AnimatedCounter": AnimatedCounter,
|
|
48321
48565
|
"AnimatedGraphic": AnimatedGraphic,
|
|
48322
48566
|
"AnimatedReveal": AnimatedReveal,
|
|
@@ -56,6 +56,7 @@ export { MathCanvas, type MathCanvasProps, type MathCurve, type MathPoint, type
|
|
|
56
56
|
export { PhysicsCanvas, type PhysicsCanvasProps, type LearningPhysicsBody, type LearningPhysicsConstraint, } from '../../learning/molecules/PhysicsCanvas';
|
|
57
57
|
export { BiologyCanvas, type BiologyCanvasProps, type BiologyNode, type BiologyEdge, } from '../../learning/molecules/BiologyCanvas';
|
|
58
58
|
export { ChemistryCanvas, type ChemistryCanvasProps, type ChemistryAtom, type ChemistryBond, type ChemistryArrow, } from '../../learning/molecules/ChemistryCanvas';
|
|
59
|
+
export { AlgorithmCanvas, type AlgorithmCanvasProps, type AlgorithmBar, type AlgorithmCell, type AlgorithmPointer, } from '../../learning/molecules/AlgorithmCanvas';
|
|
59
60
|
export { GraphView, type GraphViewProps, type GraphViewNode, type GraphViewEdge } from './GraphView';
|
|
60
61
|
export { MapView, type MapViewProps, type MapMarkerData, type MapRouteData, type MapRouteWaypoint } from './MapView';
|
|
61
62
|
export { NumberStepper, type NumberStepperProps, type NumberStepperSize } from './NumberStepper';
|
|
@@ -134,12 +134,12 @@ export interface Canvas2DProps {
|
|
|
134
134
|
spriteMaxWidthRatio?: number;
|
|
135
135
|
/** Override for the diamond-top Y offset within the tile sprite. */
|
|
136
136
|
diamondTopY?: number;
|
|
137
|
-
/** Resolve terrain sprite
|
|
138
|
-
getTerrainSprite?: (terrain: string) => string | undefined;
|
|
139
|
-
/** Resolve feature sprite
|
|
140
|
-
getFeatureSprite?: (featureType: string) => string | undefined;
|
|
141
|
-
/** Resolve unit static sprite URL */
|
|
142
|
-
getUnitSprite?: (unit: IsometricUnit) => string | undefined;
|
|
137
|
+
/** Resolve terrain sprite from terrain key — a bare URL or an Asset (atlas sub-texture ref). */
|
|
138
|
+
getTerrainSprite?: (terrain: string) => string | Asset | undefined;
|
|
139
|
+
/** Resolve feature sprite from feature type key — a bare URL or an Asset. */
|
|
140
|
+
getFeatureSprite?: (featureType: string) => string | Asset | undefined;
|
|
141
|
+
/** Resolve unit static sprite — a bare URL or an Asset. */
|
|
142
|
+
getUnitSprite?: (unit: IsometricUnit) => string | Asset | undefined;
|
|
143
143
|
/** Resolve animated sprite sheet frame for a unit */
|
|
144
144
|
resolveUnitFrame?: (unitId: string) => ResolvedFrame | null;
|
|
145
145
|
/** Additional sprite URLs to preload (e.g., effect sprites) */
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* atlasSlice — the ONLY place a packed sheet is cut at runtime.
|
|
3
|
+
*
|
|
4
|
+
* An `Asset` may carry `{ url: <sheet.png>, atlas: <atlas.json>, sprite: <name|index> }`. This
|
|
5
|
+
* module fetches + caches the atlas JSON (a `TextureAtlas` of named sub-rects or a uniform
|
|
6
|
+
* `Tilesheet`) and resolves the source rectangle for a `sprite`, so the canvas can `drawImage`
|
|
7
|
+
* exactly that sub-region of the sheet. Coexists with the whole-PNG path (an Asset with no
|
|
8
|
+
* `atlas`/`sprite` draws the whole image) and the `useUnitSpriteAtlas` animation path.
|
|
9
|
+
*
|
|
10
|
+
* See `docs/Almadar_Std_Assets.md` §2, §7.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
import type { TextureAtlas, Tilesheet } from '@almadar/core';
|
|
15
|
+
/** The minimal draw-relevant projection of an `Asset` — a sheet URL plus an optional atlas
|
|
16
|
+
* sub-texture reference. A full `@almadar/core` `Asset` is structurally assignable. */
|
|
17
|
+
export interface SpriteRef {
|
|
18
|
+
url?: string;
|
|
19
|
+
atlas?: string;
|
|
20
|
+
sprite?: string;
|
|
21
|
+
}
|
|
22
|
+
type ParsedAtlas = TextureAtlas | Tilesheet;
|
|
23
|
+
/**
|
|
24
|
+
* Get a parsed atlas by URL, fetching+caching on first request. Returns undefined until loaded;
|
|
25
|
+
* `onReady` fires once when the fetch resolves so the caller can trigger a re-render.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getAtlas(url: string, onReady: () => void): ParsedAtlas | undefined;
|
|
28
|
+
export interface SubRect {
|
|
29
|
+
sx: number;
|
|
30
|
+
sy: number;
|
|
31
|
+
sw: number;
|
|
32
|
+
sh: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the source rect for `sprite` in `atlas`.
|
|
36
|
+
* - `TextureAtlas`: `sprite` is a SubTexture name (e.g. `"blockBrown.png"`).
|
|
37
|
+
* - `Tilesheet`: `sprite` is `"col,row"` or a flat index into the row-major grid.
|
|
38
|
+
* Returns null if the name/index doesn't resolve.
|
|
39
|
+
*/
|
|
40
|
+
export declare function subRectFor(atlas: ParsedAtlas, sprite: string): SubRect | null;
|
|
41
|
+
/** True when the asset points into a sheet (needs slicing) rather than being a whole PNG. */
|
|
42
|
+
export declare function isAtlasAsset(asset: SpriteRef | undefined | null): boolean;
|
|
43
|
+
export interface AssetSource {
|
|
44
|
+
img: HTMLImageElement;
|
|
45
|
+
/** Sub-rect to blit; null = draw the whole image. */
|
|
46
|
+
rect: SubRect | null;
|
|
47
|
+
/** Source aspect ratio (width/height) — sub-rect for a slice, natural otherwise. */
|
|
48
|
+
aspect: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve what to draw for `asset` given its loaded sheet `img`. Returns null when the asset is
|
|
52
|
+
* an atlas ref whose JSON isn't loaded yet (or whose sprite name doesn't resolve) — `onReady`
|
|
53
|
+
* will trigger a re-render once the fetch lands. Callers use `.aspect` to size, then `blit`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveAssetSource(img: HTMLImageElement, asset: SpriteRef | undefined | null, onReady: () => void): AssetSource | null;
|
|
56
|
+
/** Draw a resolved source at `(dx,dy,dw,dh)` — sliced sub-rect or whole image. */
|
|
57
|
+
export declare function blit(ctx: CanvasRenderingContext2D, src: AssetSource, dx: number, dy: number, dw: number, dh: number): void;
|
|
58
|
+
export {};
|