@almadar/ui 5.61.0 → 5.63.1

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 (61) hide show
  1. package/dist/avl/index.cjs +3599 -1810
  2. package/dist/avl/index.js +2431 -642
  3. package/dist/components/core/atoms/Box.d.ts +2 -0
  4. package/dist/components/core/molecules/CalendarGrid.d.ts +9 -1
  5. package/dist/components/core/molecules/GraphCanvas.d.ts +4 -0
  6. package/dist/components/core/molecules/PositionedCanvas.d.ts +14 -1
  7. package/dist/components/core/molecules/ReplyTree.d.ts +13 -1
  8. package/dist/components/core/templates/index.d.ts +4 -0
  9. package/dist/components/game/molecules/CardHand.d.ts +35 -0
  10. package/dist/components/game/molecules/DialogueBox.d.ts +8 -2
  11. package/dist/components/game/molecules/index.d.ts +1 -0
  12. package/dist/components/game/molecules/three/index.cjs +283 -115
  13. package/dist/components/game/molecules/three/index.js +283 -115
  14. package/dist/components/game/organisms/CardBattlerBoard.d.ts +46 -0
  15. package/dist/components/game/organisms/CityBuilderBoard.d.ts +63 -0
  16. package/dist/components/game/organisms/TopDownShooterBoard.d.ts +64 -0
  17. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +10 -1
  18. package/dist/components/game/organisms/TraitSlot.d.ts +1 -3
  19. package/dist/components/game/organisms/VisualNovelBoard.d.ts +53 -0
  20. package/dist/components/game/organisms/hooks/useCamera.d.ts +16 -0
  21. package/dist/components/game/organisms/index.d.ts +4 -0
  22. package/dist/components/game/templates/CardBattlerTemplate.d.ts +35 -0
  23. package/dist/components/game/templates/CityBuilderTemplate.d.ts +40 -0
  24. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +5 -2
  25. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +5 -2
  26. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +5 -2
  27. package/dist/components/game/templates/TopDownShooterTemplate.d.ts +42 -0
  28. package/dist/components/game/templates/VisualNovelTemplate.d.ts +28 -0
  29. package/dist/components/game/templates/game3dAssetManifest.d.ts +50 -0
  30. package/dist/components/game/templates/index.d.ts +4 -0
  31. package/dist/components/index.cjs +3465 -1659
  32. package/dist/components/index.js +2444 -647
  33. package/dist/components/marketing/organisms/CaseStudyOrganism.d.ts +10 -1
  34. package/dist/components/marketing/organisms/FeatureGridOrganism.d.ts +10 -1
  35. package/dist/components/marketing/organisms/HeroOrganism.d.ts +24 -1
  36. package/dist/components/marketing/organisms/PricingOrganism.d.ts +14 -1
  37. package/dist/components/marketing/organisms/ShowcaseOrganism.d.ts +14 -1
  38. package/dist/components/marketing/organisms/StatsOrganism.d.ts +6 -1
  39. package/dist/components/marketing/organisms/StepFlowOrganism.d.ts +9 -1
  40. package/dist/components/marketing/organisms/TeamOrganism.d.ts +10 -1
  41. package/dist/docs/index.cjs +169 -1
  42. package/dist/docs/index.d.cts +2 -0
  43. package/dist/docs/index.js +169 -1
  44. package/dist/hooks/index.cjs +253 -1
  45. package/dist/hooks/index.d.ts +2 -0
  46. package/dist/hooks/index.js +252 -2
  47. package/dist/hooks/useCanvasGestures.d.ts +44 -0
  48. package/dist/hooks/useTapReveal.d.ts +32 -0
  49. package/dist/locales/index.cjs +315 -3
  50. package/dist/locales/index.js +315 -3
  51. package/dist/marketing/index.cjs +59 -0
  52. package/dist/marketing/index.d.cts +2 -0
  53. package/dist/marketing/index.js +59 -0
  54. package/dist/providers/index.cjs +3408 -1619
  55. package/dist/providers/index.js +2407 -618
  56. package/dist/runtime/index.cjs +3464 -1675
  57. package/dist/runtime/index.js +2411 -622
  58. package/locales/ar.json +105 -1
  59. package/locales/en.json +105 -1
  60. package/locales/sl.json +105 -1
  61. package/package.json +2 -2
@@ -377,12 +377,18 @@ function ModelLoader({
377
377
  });
378
378
  return cloned;
379
379
  }, [loadedModel, castShadow, receiveShadow]);
380
+ const normFactor = useMemo(() => {
381
+ if (!model) return 1;
382
+ const box = new THREE6.Box3().setFromObject(model);
383
+ const size = new THREE6.Vector3();
384
+ box.getSize(size);
385
+ const maxDim = Math.max(size.x, size.y, size.z);
386
+ return maxDim > 0 && Number.isFinite(maxDim) ? 1 / maxDim : 1;
387
+ }, [model]);
380
388
  const scaleArray = useMemo(() => {
381
- if (typeof scale === "number") {
382
- return [scale, scale, scale];
383
- }
384
- return scale;
385
- }, [scale]);
389
+ const base = typeof scale === "number" ? [scale, scale, scale] : scale;
390
+ return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
391
+ }, [scale, normFactor]);
386
392
  const rotationRad = useMemo(() => {
387
393
  return [
388
394
  rotation[0] * Math.PI / 180,
@@ -2566,6 +2572,29 @@ var GameCanvas3D = forwardRef(
2566
2572
  const controlsRef = useRef(null);
2567
2573
  const [hoveredTile, setHoveredTile] = useState(null);
2568
2574
  const [internalError, setInternalError] = useState(null);
2575
+ useEffect(() => {
2576
+ const el = containerRef.current;
2577
+ if (!el) return;
2578
+ let node = el;
2579
+ let depth = 0;
2580
+ while (node && depth < 8) {
2581
+ const cs = window.getComputedStyle(node);
2582
+ const rect = node.getBoundingClientRect();
2583
+ console.log("[almadar:ui:game:3d-height]", {
2584
+ depth,
2585
+ tag: node.tagName,
2586
+ id: node.id || void 0,
2587
+ className: node.className?.slice?.(0, 60) || void 0,
2588
+ rectHeight: rect.height,
2589
+ computedHeight: cs.height,
2590
+ computedMinHeight: cs.minHeight,
2591
+ computedFlex: cs.flex,
2592
+ computedOverflow: cs.overflow
2593
+ });
2594
+ node = node.parentElement;
2595
+ depth++;
2596
+ }
2597
+ }, []);
2569
2598
  const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
2570
2599
  const preloadUrls = useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
2571
2600
  const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
@@ -2955,7 +2984,7 @@ var GameCanvas3D = forwardRef(
2955
2984
  {
2956
2985
  ref: containerRef,
2957
2986
  className: cn("game-canvas-3d relative w-full overflow-hidden", className),
2958
- style: { minHeight: "85vh" },
2987
+ style: { height: "85vh" },
2959
2988
  "data-orientation": orientation,
2960
2989
  "data-camera-mode": cameraMode,
2961
2990
  "data-overlay": overlay,
@@ -2970,7 +2999,7 @@ var GameCanvas3D = forwardRef(
2970
2999
  near: 0.1,
2971
3000
  far: 1e3
2972
3001
  },
2973
- style: { background: backgroundColor, height: "85vh", width: "100%" },
3002
+ style: { background: backgroundColor, position: "absolute", inset: 0 },
2974
3003
  onClick: (e) => {
2975
3004
  if (e.target === e.currentTarget) {
2976
3005
  eventHandlers.handleCanvasClick(e);
@@ -3048,6 +3077,9 @@ var GameCanvas3D = forwardRef(
3048
3077
  target: cameraTarget,
3049
3078
  enableDamping: true,
3050
3079
  dampingFactor: 0.05,
3080
+ enableZoom: true,
3081
+ enablePan: true,
3082
+ touches: { ONE: THREE6.TOUCH.ROTATE, TWO: THREE6.TOUCH.DOLLY_PAN },
3051
3083
  minDistance: 2,
3052
3084
  maxDistance: 100,
3053
3085
  maxPolarAngle: Math.PI / 2 - 0.1
@@ -4385,6 +4417,51 @@ function GameBoard3D({
4385
4417
  ] });
4386
4418
  }
4387
4419
  GameBoard3D.displayName = "GameBoard3D";
4420
+ function useTapReveal(options = {}) {
4421
+ const { onReveal, onDismiss, refs, enabled = true } = options;
4422
+ const [revealed, setRevealed] = useState(false);
4423
+ const onRevealRef = useRef(onReveal);
4424
+ const onDismissRef = useRef(onDismiss);
4425
+ onRevealRef.current = onReveal;
4426
+ onDismissRef.current = onDismiss;
4427
+ const reveal = useCallback(() => {
4428
+ setRevealed((wasRevealed) => {
4429
+ if (!wasRevealed) onRevealRef.current?.();
4430
+ return true;
4431
+ });
4432
+ }, []);
4433
+ const dismiss = useCallback(() => {
4434
+ setRevealed((wasRevealed) => {
4435
+ if (wasRevealed) onDismissRef.current?.();
4436
+ return false;
4437
+ });
4438
+ }, []);
4439
+ const onPointerDown = useCallback(
4440
+ (e) => {
4441
+ if (!enabled || e.pointerType === "mouse") return;
4442
+ if (revealed) dismiss();
4443
+ else reveal();
4444
+ },
4445
+ [enabled, revealed, reveal, dismiss]
4446
+ );
4447
+ useEffect(() => {
4448
+ if (!revealed || typeof document === "undefined") return;
4449
+ const onDocDown = (ev) => {
4450
+ const target = ev.target;
4451
+ if (!(target instanceof Node)) return;
4452
+ const inside = (refs ?? []).some((r) => r.current?.contains(target));
4453
+ if (!inside) dismiss();
4454
+ };
4455
+ const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
4456
+ return () => {
4457
+ clearTimeout(id);
4458
+ document.removeEventListener("pointerdown", onDocDown);
4459
+ };
4460
+ }, [revealed, refs, dismiss]);
4461
+ return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
4462
+ }
4463
+
4464
+ // components/core/atoms/Box.tsx
4388
4465
  var paddingStyles = {
4389
4466
  none: "p-0",
4390
4467
  xs: "p-1",
@@ -4510,10 +4587,12 @@ var Box = React11.forwardRef(
4510
4587
  action,
4511
4588
  actionPayload,
4512
4589
  hoverEvent,
4590
+ tapReveal = true,
4513
4591
  maxWidth,
4514
4592
  onClick,
4515
4593
  onMouseEnter,
4516
4594
  onMouseLeave,
4595
+ onPointerDown,
4517
4596
  ...rest
4518
4597
  }, ref) => {
4519
4598
  const eventBus = useEventBus();
@@ -4536,6 +4615,19 @@ var Box = React11.forwardRef(
4536
4615
  }
4537
4616
  onMouseLeave?.(e);
4538
4617
  }, [hoverEvent, eventBus, onMouseLeave]);
4618
+ const { triggerProps } = useTapReveal({
4619
+ enabled: tapReveal && !!hoverEvent,
4620
+ onReveal: useCallback(() => {
4621
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
4622
+ }, [hoverEvent, eventBus]),
4623
+ onDismiss: useCallback(() => {
4624
+ if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
4625
+ }, [hoverEvent, eventBus])
4626
+ });
4627
+ const handlePointerDown = useCallback((e) => {
4628
+ if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
4629
+ onPointerDown?.(e);
4630
+ }, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
4539
4631
  const isClickable = action || onClick;
4540
4632
  return React11.createElement(
4541
4633
  Component2,
@@ -4563,6 +4655,7 @@ var Box = React11.forwardRef(
4563
4655
  onClick: isClickable ? handleClick : void 0,
4564
4656
  onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
4565
4657
  onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
4658
+ onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
4566
4659
  style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
4567
4660
  ...rest
4568
4661
  },
@@ -4571,36 +4664,113 @@ var Box = React11.forwardRef(
4571
4664
  }
4572
4665
  );
4573
4666
  Box.displayName = "Box";
4574
- var CDN = "https://almadar-kflow-assets.web.app/shared/3d/dungeon/floor";
4575
- var FLOOR_WALL = `${CDN}/template-floor-detail-a.glb`;
4576
- var FLOOR_DIRT = `${CDN}/template-floor-detail.glb`;
4577
- var FLOOR_OPEN = `${CDN}/template-floor.glb`;
4578
- var DEFAULT_3D_BATTLE_TILES = [
4579
- { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4580
- { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4581
- { id: "t20", x: 2, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4582
- { id: "t30", x: 3, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4583
- { id: "t40", x: 4, y: 0, z: 0, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4584
- { id: "t01", x: 0, y: 1, z: 1, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4585
- { id: "t11", x: 1, y: 1, z: 1, type: "dirt", passable: true, modelUrl: FLOOR_DIRT },
4586
- { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4587
- { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4588
- { id: "t41", x: 4, y: 1, z: 1, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4589
- { id: "t02", x: 0, y: 2, z: 2, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4590
- { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4591
- { id: "t22", x: 2, y: 2, z: 2, type: "dirt", passable: true, modelUrl: FLOOR_DIRT },
4592
- { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4593
- { id: "t42", x: 4, y: 2, z: 2, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4594
- { id: "t03", x: 0, y: 3, z: 3, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4595
- { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4596
- { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN },
4597
- { id: "t33", x: 3, y: 3, z: 3, type: "dirt", passable: true, modelUrl: FLOOR_DIRT },
4598
- { id: "t43", x: 4, y: 3, z: 3, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4599
- { id: "t04", x: 0, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4600
- { id: "t14", x: 1, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4601
- { id: "t24", x: 2, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4602
- { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL },
4603
- { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false, modelUrl: FLOOR_WALL }
4667
+
4668
+ // components/game/templates/game3dAssetManifest.ts
4669
+ function resolveManifestUrl(manifest, relative) {
4670
+ if (relative == null) return void 0;
4671
+ if (/^https?:\/\//.test(relative)) return relative;
4672
+ const base = manifest?.baseUrl;
4673
+ if (base == null) return relative;
4674
+ const cleanBase = base.replace(/\/$/, "");
4675
+ const cleanRel = relative.replace(/^\//, "");
4676
+ return `${cleanBase}/${cleanRel}`;
4677
+ }
4678
+ function tileKind(type, passable) {
4679
+ if (passable === false) return "wall";
4680
+ if (type === "dirt" || type === "road") return "dirt";
4681
+ return "open";
4682
+ }
4683
+ function resolveTilesWithModels(layout, manifest) {
4684
+ return layout.map((t) => {
4685
+ const kind = t.kind || tileKind(t.type, t.passable);
4686
+ return {
4687
+ id: t.id,
4688
+ x: t.x,
4689
+ y: t.y,
4690
+ z: t.z,
4691
+ type: t.type,
4692
+ passable: t.passable,
4693
+ modelUrl: resolveManifestUrl(manifest, manifest?.models?.[kind])
4694
+ };
4695
+ });
4696
+ }
4697
+ function resolvePropTiles(tiles, manifest) {
4698
+ return tiles.map((t) => {
4699
+ const kind = tileKind(t.type ?? "", t.passable);
4700
+ return {
4701
+ ...t,
4702
+ modelUrl: t.modelUrl ?? resolveManifestUrl(manifest, manifest?.models?.[kind])
4703
+ };
4704
+ });
4705
+ }
4706
+ function resolveEntityTiles(entity, manifest) {
4707
+ const row = boardEntity(entity);
4708
+ if (!row) return [];
4709
+ return rows(row.tiles).map((r) => {
4710
+ const type = str(r.type) || str(r.terrain);
4711
+ const passable = r.passable !== false;
4712
+ const kind = tileKind(type, passable);
4713
+ const explicitModel = r.modelUrl == null ? void 0 : str(r.modelUrl);
4714
+ return {
4715
+ id: r.id == null ? void 0 : str(r.id),
4716
+ x: num(r.x),
4717
+ y: num(r.y),
4718
+ z: r.z == null ? void 0 : num(r.z),
4719
+ type,
4720
+ passable,
4721
+ modelUrl: explicitModel ?? resolveManifestUrl(manifest, manifest?.models?.[kind])
4722
+ };
4723
+ });
4724
+ }
4725
+ function resolveFeaturesWithModels(features, manifest) {
4726
+ return features.map((f) => ({
4727
+ ...f,
4728
+ assetUrl: f.assetUrl ?? resolveManifestUrl(manifest, manifest?.features?.[f.type])
4729
+ }));
4730
+ }
4731
+ function resolveEntityFeatures(entity, manifest) {
4732
+ const row = boardEntity(entity);
4733
+ if (!row) return [];
4734
+ return rows(row.features).map((r) => {
4735
+ const type = str(r.type);
4736
+ const explicit = r.assetUrl == null ? void 0 : str(r.assetUrl);
4737
+ return {
4738
+ id: r.id == null ? void 0 : str(r.id),
4739
+ x: num(r.x),
4740
+ y: num(r.y),
4741
+ z: r.z == null ? void 0 : num(r.z),
4742
+ type,
4743
+ color: r.color == null ? void 0 : str(r.color),
4744
+ assetUrl: explicit ?? resolveManifestUrl(manifest, manifest?.features?.[type])
4745
+ };
4746
+ });
4747
+ }
4748
+ var DEFAULT_3D_BATTLE_LAYOUT = [
4749
+ { id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4750
+ { id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4751
+ { id: "t20", x: 2, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4752
+ { id: "t30", x: 3, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4753
+ { id: "t40", x: 4, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
4754
+ { id: "t01", x: 0, y: 1, z: 1, type: "stone", passable: false, kind: "wall" },
4755
+ { id: "t11", x: 1, y: 1, z: 1, type: "dirt", passable: true, kind: "dirt" },
4756
+ { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4757
+ { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4758
+ { id: "t41", x: 4, y: 1, z: 1, type: "stone", passable: false, kind: "wall" },
4759
+ { id: "t02", x: 0, y: 2, z: 2, type: "stone", passable: false, kind: "wall" },
4760
+ { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4761
+ { id: "t22", x: 2, y: 2, z: 2, type: "dirt", passable: true, kind: "dirt" },
4762
+ { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4763
+ { id: "t42", x: 4, y: 2, z: 2, type: "stone", passable: false, kind: "wall" },
4764
+ { id: "t03", x: 0, y: 3, z: 3, type: "stone", passable: false, kind: "wall" },
4765
+ { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4766
+ { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4767
+ { id: "t33", x: 3, y: 3, z: 3, type: "dirt", passable: true, kind: "dirt" },
4768
+ { id: "t43", x: 4, y: 3, z: 3, type: "stone", passable: false, kind: "wall" },
4769
+ { id: "t04", x: 0, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4770
+ { id: "t14", x: 1, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4771
+ { id: "t24", x: 2, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4772
+ { id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
4773
+ { id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false, kind: "wall" }
4604
4774
  ];
4605
4775
  var DEFAULT_3D_BATTLE_FEATURES = [
4606
4776
  { id: "f1", x: 2, y: 2, z: 2, type: "gold_mine", color: "#f4c542" },
@@ -4608,8 +4778,9 @@ var DEFAULT_3D_BATTLE_FEATURES = [
4608
4778
  ];
4609
4779
  function GameCanvas3DBattleTemplate({
4610
4780
  entity,
4611
- tiles: propTiles = DEFAULT_3D_BATTLE_TILES,
4781
+ tiles: propTiles,
4612
4782
  features: propFeatures = DEFAULT_3D_BATTLE_FEATURES,
4783
+ assetManifest,
4613
4784
  cameraMode = "perspective",
4614
4785
  backgroundColor = "#2a1a1a",
4615
4786
  tileClickEvent,
@@ -4623,11 +4794,12 @@ function GameCanvas3DBattleTemplate({
4623
4794
  scale,
4624
4795
  className
4625
4796
  }) {
4626
- const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
4627
- const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
4628
- const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
4629
- const currentTurn = resolved?.currentTeam;
4630
- const round = resolved?.turn == null ? void 0 : Number(resolved.turn);
4797
+ const row = boardEntity(entity);
4798
+ const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
4799
+ const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_BATTLE_LAYOUT, assetManifest);
4800
+ const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
4801
+ const currentTurn = row?.currentTeam == null ? void 0 : String(row.currentTeam);
4802
+ const round = row?.turn == null ? void 0 : Number(row.turn);
4631
4803
  return /* @__PURE__ */ jsxs(
4632
4804
  Box,
4633
4805
  {
@@ -4673,36 +4845,32 @@ function GameCanvas3DBattleTemplate({
4673
4845
  );
4674
4846
  }
4675
4847
  GameCanvas3DBattleTemplate.displayName = "GameCanvas3DBattleTemplate";
4676
- var CDN2 = "https://almadar-kflow-assets.web.app/shared/3d/dungeon/floor";
4677
- var FLOOR_WALL2 = `${CDN2}/template-floor-detail-a.glb`;
4678
- var FLOOR_DIRT2 = `${CDN2}/template-floor-detail.glb`;
4679
- var FLOOR_OPEN2 = `${CDN2}/template-floor.glb`;
4680
- var DEFAULT_3D_CASTLE_TILES = [
4681
- { id: "t00", x: 0, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4682
- { id: "t10", x: 1, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4683
- { id: "t20", x: 2, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4684
- { id: "t30", x: 3, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4685
- { id: "t40", x: 4, y: 0, z: 0, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4686
- { id: "t01", x: 0, y: 1, z: 1, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4687
- { id: "t11", x: 1, y: 1, z: 1, type: "floor", passable: true, modelUrl: FLOOR_DIRT2 },
4688
- { id: "t21", x: 2, y: 1, z: 1, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4689
- { id: "t31", x: 3, y: 1, z: 1, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4690
- { id: "t41", x: 4, y: 1, z: 1, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4691
- { id: "t02", x: 0, y: 2, z: 2, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4692
- { id: "t12", x: 1, y: 2, z: 2, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4693
- { id: "t22", x: 2, y: 2, z: 2, type: "floor", passable: true, modelUrl: FLOOR_DIRT2 },
4694
- { id: "t32", x: 3, y: 2, z: 2, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4695
- { id: "t42", x: 4, y: 2, z: 2, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4696
- { id: "t03", x: 0, y: 3, z: 3, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4697
- { id: "t13", x: 1, y: 3, z: 3, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4698
- { id: "t23", x: 2, y: 3, z: 3, type: "floor", passable: true, modelUrl: FLOOR_OPEN2 },
4699
- { id: "t33", x: 3, y: 3, z: 3, type: "floor", passable: true, modelUrl: FLOOR_DIRT2 },
4700
- { id: "t43", x: 4, y: 3, z: 3, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4701
- { id: "t04", x: 0, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4702
- { id: "t14", x: 1, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4703
- { id: "t24", x: 2, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4704
- { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 },
4705
- { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false, modelUrl: FLOOR_WALL2 }
4848
+ var DEFAULT_3D_CASTLE_LAYOUT = [
4849
+ { id: "t00", x: 0, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4850
+ { id: "t10", x: 1, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4851
+ { id: "t20", x: 2, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4852
+ { id: "t30", x: 3, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4853
+ { id: "t40", x: 4, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
4854
+ { id: "t01", x: 0, y: 1, z: 1, type: "wall", passable: false, kind: "wall" },
4855
+ { id: "t11", x: 1, y: 1, z: 1, type: "floor", passable: true, kind: "dirt" },
4856
+ { id: "t21", x: 2, y: 1, z: 1, type: "floor", passable: true, kind: "open" },
4857
+ { id: "t31", x: 3, y: 1, z: 1, type: "floor", passable: true, kind: "open" },
4858
+ { id: "t41", x: 4, y: 1, z: 1, type: "wall", passable: false, kind: "wall" },
4859
+ { id: "t02", x: 0, y: 2, z: 2, type: "wall", passable: false, kind: "wall" },
4860
+ { id: "t12", x: 1, y: 2, z: 2, type: "floor", passable: true, kind: "open" },
4861
+ { id: "t22", x: 2, y: 2, z: 2, type: "floor", passable: true, kind: "dirt" },
4862
+ { id: "t32", x: 3, y: 2, z: 2, type: "floor", passable: true, kind: "open" },
4863
+ { id: "t42", x: 4, y: 2, z: 2, type: "wall", passable: false, kind: "wall" },
4864
+ { id: "t03", x: 0, y: 3, z: 3, type: "wall", passable: false, kind: "wall" },
4865
+ { id: "t13", x: 1, y: 3, z: 3, type: "floor", passable: true, kind: "open" },
4866
+ { id: "t23", x: 2, y: 3, z: 3, type: "floor", passable: true, kind: "open" },
4867
+ { id: "t33", x: 3, y: 3, z: 3, type: "floor", passable: true, kind: "dirt" },
4868
+ { id: "t43", x: 4, y: 3, z: 3, type: "wall", passable: false, kind: "wall" },
4869
+ { id: "t04", x: 0, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4870
+ { id: "t14", x: 1, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4871
+ { id: "t24", x: 2, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4872
+ { id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
4873
+ { id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false, kind: "wall" }
4706
4874
  ];
4707
4875
  var DEFAULT_3D_CASTLE_FEATURES = [
4708
4876
  { id: "f1", x: 2, y: 2, z: 2, type: "chest", color: "#f4c542" },
@@ -4710,8 +4878,9 @@ var DEFAULT_3D_CASTLE_FEATURES = [
4710
4878
  ];
4711
4879
  function GameCanvas3DCastleTemplate({
4712
4880
  entity,
4713
- tiles: propTiles = DEFAULT_3D_CASTLE_TILES,
4881
+ tiles: propTiles,
4714
4882
  features: propFeatures = DEFAULT_3D_CASTLE_FEATURES,
4883
+ assetManifest,
4715
4884
  cameraMode = "isometric",
4716
4885
  backgroundColor = "#1e1e2e",
4717
4886
  buildingClickEvent,
@@ -4726,13 +4895,14 @@ function GameCanvas3DCastleTemplate({
4726
4895
  scale,
4727
4896
  className
4728
4897
  }) {
4729
- const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
4730
- const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
4731
- const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
4732
- const name = resolved?.name == null ? void 0 : String(resolved.name);
4733
- const level = resolved?.level == null ? void 0 : Number(resolved.level);
4734
- const owner = resolved?.owner == null ? void 0 : String(resolved.owner);
4735
- const unitCount = resolved && Array.isArray(resolved.units) ? resolved.units.length : 0;
4898
+ const row = boardEntity(entity);
4899
+ const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
4900
+ const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_CASTLE_LAYOUT, assetManifest);
4901
+ const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
4902
+ const name = row?.name == null ? void 0 : str(row.name);
4903
+ const level = row?.level == null ? void 0 : num(row.level);
4904
+ const owner = row?.owner == null ? void 0 : str(row.owner);
4905
+ const unitCount = row && Array.isArray(row.units) ? row.units.length : 0;
4736
4906
  return /* @__PURE__ */ jsxs(
4737
4907
  VStack,
4738
4908
  {
@@ -4778,36 +4948,32 @@ function GameCanvas3DCastleTemplate({
4778
4948
  );
4779
4949
  }
4780
4950
  GameCanvas3DCastleTemplate.displayName = "GameCanvas3DCastleTemplate";
4781
- var CDN3 = "https://almadar-kflow-assets.web.app/shared/3d/dungeon/floor";
4782
- var FLOOR_WALL3 = `${CDN3}/template-floor-detail-a.glb`;
4783
- var FLOOR_DIRT3 = `${CDN3}/template-floor-detail.glb`;
4784
- var FLOOR_OPEN3 = `${CDN3}/template-floor.glb`;
4785
- var DEFAULT_3D_WORLDMAP_TILES = [
4786
- { id: "t00", x: 0, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4787
- { id: "t10", x: 1, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4788
- { id: "t20", x: 2, y: 0, z: 0, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4789
- { id: "t30", x: 3, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4790
- { id: "t40", x: 4, y: 0, z: 0, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4791
- { id: "t01", x: 0, y: 1, z: 1, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4792
- { id: "t11", x: 1, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4793
- { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4794
- { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4795
- { id: "t41", x: 4, y: 1, z: 1, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4796
- { id: "t02", x: 0, y: 2, z: 2, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4797
- { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4798
- { id: "t22", x: 2, y: 2, z: 2, type: "road", passable: true, modelUrl: FLOOR_DIRT3 },
4799
- { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4800
- { id: "t42", x: 4, y: 2, z: 2, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4801
- { id: "t03", x: 0, y: 3, z: 3, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4802
- { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4803
- { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4804
- { id: "t33", x: 3, y: 3, z: 3, type: "road", passable: true, modelUrl: FLOOR_DIRT3 },
4805
- { id: "t43", x: 4, y: 3, z: 3, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4806
- { id: "t04", x: 0, y: 4, z: 4, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4807
- { id: "t14", x: 1, y: 4, z: 4, type: "water", passable: false, modelUrl: FLOOR_WALL3 },
4808
- { id: "t24", x: 2, y: 4, z: 4, type: "grass", passable: true, modelUrl: FLOOR_OPEN3 },
4809
- { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 },
4810
- { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false, modelUrl: FLOOR_WALL3 }
4951
+ var DEFAULT_3D_WORLDMAP_LAYOUT = [
4952
+ { id: "t00", x: 0, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4953
+ { id: "t10", x: 1, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4954
+ { id: "t20", x: 2, y: 0, z: 0, type: "water", passable: false, kind: "wall" },
4955
+ { id: "t30", x: 3, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4956
+ { id: "t40", x: 4, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
4957
+ { id: "t01", x: 0, y: 1, z: 1, type: "mountain", passable: false, kind: "wall" },
4958
+ { id: "t11", x: 1, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4959
+ { id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4960
+ { id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
4961
+ { id: "t41", x: 4, y: 1, z: 1, type: "water", passable: false, kind: "wall" },
4962
+ { id: "t02", x: 0, y: 2, z: 2, type: "water", passable: false, kind: "wall" },
4963
+ { id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4964
+ { id: "t22", x: 2, y: 2, z: 2, type: "road", passable: true, kind: "dirt" },
4965
+ { id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
4966
+ { id: "t42", x: 4, y: 2, z: 2, type: "mountain", passable: false, kind: "wall" },
4967
+ { id: "t03", x: 0, y: 3, z: 3, type: "mountain", passable: false, kind: "wall" },
4968
+ { id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4969
+ { id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
4970
+ { id: "t33", x: 3, y: 3, z: 3, type: "road", passable: true, kind: "dirt" },
4971
+ { id: "t43", x: 4, y: 3, z: 3, type: "mountain", passable: false, kind: "wall" },
4972
+ { id: "t04", x: 0, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" },
4973
+ { id: "t14", x: 1, y: 4, z: 4, type: "water", passable: false, kind: "wall" },
4974
+ { id: "t24", x: 2, y: 4, z: 4, type: "grass", passable: true, kind: "open" },
4975
+ { id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" },
4976
+ { id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" }
4811
4977
  ];
4812
4978
  var DEFAULT_3D_WORLDMAP_FEATURES = [
4813
4979
  { id: "f1", x: 2, y: 2, z: 2, type: "capital", color: "#f4c542" },
@@ -4815,8 +4981,9 @@ var DEFAULT_3D_WORLDMAP_FEATURES = [
4815
4981
  ];
4816
4982
  function GameCanvas3DWorldMapTemplate({
4817
4983
  entity,
4818
- tiles: propTiles = DEFAULT_3D_WORLDMAP_TILES,
4984
+ tiles: propTiles,
4819
4985
  features: propFeatures = DEFAULT_3D_WORLDMAP_FEATURES,
4986
+ assetManifest,
4820
4987
  cameraMode = "isometric",
4821
4988
  backgroundColor = "#1a1a2e",
4822
4989
  tileClickEvent,
@@ -4830,9 +4997,10 @@ function GameCanvas3DWorldMapTemplate({
4830
4997
  scale,
4831
4998
  className
4832
4999
  }) {
4833
- const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
4834
- const tiles = resolved ? Array.isArray(resolved.tiles) ? resolved.tiles : [] : propTiles;
4835
- const features = resolved ? Array.isArray(resolved.features) ? resolved.features : [] : propFeatures;
5000
+ const row = boardEntity(entity);
5001
+ const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
5002
+ const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_WORLDMAP_LAYOUT, assetManifest);
5003
+ const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
4836
5004
  return (
4837
5005
  /* GameBoard3D reads selectedUnitId/validMoves/attackTargets from entity */
4838
5006
  /* @__PURE__ */ jsx(
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import type { AssetUrl, EventEmit, EntityRow } from '@almadar/core';
3
+ import type { DisplayStateProps } from '../../core/organisms/types';
4
+ /** Manifest of asset base-url + per-card sprite map (UI value DTO). */
5
+ type CardBattlerAssetManifest = {
6
+ baseUrl?: AssetUrl;
7
+ cards?: Record<string, AssetUrl>;
8
+ };
9
+ /** One card as carried on the entity / passed as a prop. */
10
+ export interface CardBattlerCard {
11
+ id: string;
12
+ iconKey?: string;
13
+ title?: string;
14
+ cost?: number;
15
+ attack?: number;
16
+ defense?: number;
17
+ }
18
+ export interface CardBattlerBoardProps extends DisplayStateProps {
19
+ entity?: EntityRow | readonly EntityRow[];
20
+ /** Cards remaining in the draw deck (count-only view). */
21
+ deck?: CardBattlerCard[];
22
+ /** Cards currently in the player's hand. */
23
+ hand?: CardBattlerCard[];
24
+ /** Cards played onto the board this match. */
25
+ board?: CardBattlerCard[];
26
+ mana?: number;
27
+ maxMana?: number;
28
+ turn?: number;
29
+ result?: 'none' | 'won' | 'lost';
30
+ /** Base-url + per-iconKey card sprite map (organism owns asset choice). */
31
+ assetManifest?: CardBattlerAssetManifest;
32
+ playCardEvent?: EventEmit<{
33
+ cardId: string;
34
+ }>;
35
+ endTurnEvent?: EventEmit<Record<string, never>>;
36
+ playAgainEvent?: EventEmit<Record<string, never>>;
37
+ gameEndEvent?: EventEmit<{
38
+ result: 'won' | 'lost';
39
+ }>;
40
+ className?: string;
41
+ }
42
+ export declare function CardBattlerBoard({ entity, deck: propDeck, hand: propHand, board: propBoard, mana: propMana, maxMana: propMaxMana, turn: propTurn, result: propResult, assetManifest: propAssetManifest, playCardEvent, endTurnEvent, playAgainEvent, gameEndEvent, className, }: CardBattlerBoardProps): React.JSX.Element;
43
+ export declare namespace CardBattlerBoard {
44
+ var displayName: string;
45
+ }
46
+ export default CardBattlerBoard;