@almadar/ui 5.28.1 → 5.28.3

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.
@@ -9,7 +9,9 @@
9
9
  * @packageDocumentation
10
10
  */
11
11
  import React from 'react';
12
+ import type { WorldMapBoardProps } from '../organisms/WorldMapBoard';
12
13
  import type { TemplateProps } from '../../core/templates/types';
14
+ import type { IsometricTile, IsometricUnit, IsometricFeature } from '../organisms/types/isometric';
13
15
  export type { WorldMapSlotContext } from '../organisms/WorldMapBoard';
14
16
  export interface WorldMapTemplateProps extends TemplateProps {
15
17
  /** Canvas render scale */
@@ -20,8 +22,16 @@ export interface WorldMapTemplateProps extends TemplateProps {
20
22
  diamondTopY?: number;
21
23
  /** Allow selecting / moving ALL heroes (including enemy). For testing. */
22
24
  allowMoveAllHeroes?: boolean;
25
+ /** Direct tile data — forwarded to WorldMapBoard; takes priority over entity. */
26
+ tiles?: IsometricTile[];
27
+ /** Direct unit data — forwarded to WorldMapBoard; takes priority over entity. */
28
+ units?: IsometricUnit[];
29
+ /** Direct feature data — forwarded to WorldMapBoard; takes priority over entity. */
30
+ features?: IsometricFeature[];
31
+ /** Direct asset manifest — forwarded to WorldMapBoard; takes priority over entity. */
32
+ assetManifest?: WorldMapBoardProps['assetManifest'];
23
33
  }
24
- export declare function WorldMapTemplate({ entity, scale, unitScale, diamondTopY, allowMoveAllHeroes, className, }: WorldMapTemplateProps): React.JSX.Element;
34
+ export declare function WorldMapTemplate({ entity, scale, unitScale, diamondTopY, allowMoveAllHeroes, tiles, units, features, assetManifest, className, }: WorldMapTemplateProps): React.JSX.Element;
25
35
  export declare namespace WorldMapTemplate {
26
36
  var displayName: string;
27
37
  }
@@ -9302,7 +9302,7 @@ function IsometricCanvas({
9302
9302
  // Rendering options
9303
9303
  scale = 0.4,
9304
9304
  debug: debug2 = false,
9305
- backgroundImage,
9305
+ backgroundImage = "https://almadar-kflow-assets.web.app/shared/scenes/court.png",
9306
9306
  showMinimap = true,
9307
9307
  enableCamera = true,
9308
9308
  unitScale = 1,
@@ -9317,7 +9317,7 @@ function IsometricCanvas({
9317
9317
  // Tuning
9318
9318
  diamondTopY: diamondTopYProp,
9319
9319
  // Remote asset loading
9320
- assetBaseUrl,
9320
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
9321
9321
  assetManifest
9322
9322
  }) {
9323
9323
  const tilesProp = Array.isArray(_tilesPropRaw) ? _tilesPropRaw : [];
@@ -10077,6 +10077,10 @@ var init_boardEntity = __esm({
10077
10077
  });
10078
10078
  function BattleBoard({
10079
10079
  entity,
10080
+ tiles: propTiles,
10081
+ units: propUnits,
10082
+ features: propFeatures,
10083
+ assetManifest: propAssetManifest,
10080
10084
  scale = 0.45,
10081
10085
  unitScale = 1,
10082
10086
  header,
@@ -10102,11 +10106,11 @@ function BattleBoard({
10102
10106
  className
10103
10107
  }) {
10104
10108
  const board = boardEntity(entity) ?? {};
10105
- const tiles = Array.isArray(board.tiles) ? board.tiles : [];
10106
- const features = Array.isArray(board.features) ? board.features : [];
10109
+ const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10110
+ const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
10107
10111
  const boardWidth = num(board.boardWidth, 8);
10108
10112
  const boardHeight = num(board.boardHeight, 6);
10109
- const assetManifest = board.assetManifest;
10113
+ const assetManifest = propAssetManifest ?? board.assetManifest;
10110
10114
  const backgroundImage = board.backgroundImage;
10111
10115
  const units = rows(board.units);
10112
10116
  const selectedUnitId = board.selectedUnitId ?? null;
@@ -10198,7 +10202,7 @@ function BattleBoard({
10198
10202
  }, 16);
10199
10203
  return () => clearInterval(interval);
10200
10204
  }, []);
10201
- const isoUnits = React74.useMemo(() => {
10205
+ const derivedIsoUnits = React74.useMemo(() => {
10202
10206
  return units.filter((u) => unitHealth(u) > 0).map((unit) => {
10203
10207
  const id = str(unit.id);
10204
10208
  const pos = movingPositions.get(id) ?? unitPosition(unit);
@@ -10222,6 +10226,7 @@ function BattleBoard({
10222
10226
  };
10223
10227
  });
10224
10228
  }, [units, movingPositions]);
10229
+ const isoUnits = propUnits ?? derivedIsoUnits;
10225
10230
  const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
10226
10231
  const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
10227
10232
  const tileToScreen = React74.useCallback(
@@ -17425,16 +17430,21 @@ function EmojiEffect({
17425
17430
  }
17426
17431
  );
17427
17432
  }
17428
- function CanvasEffect(props) {
17433
+ function CanvasEffect({
17434
+ effectSpriteUrl = "https://almadar-kflow-assets.web.app/shared/effects/gas/gas00.png",
17435
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/effects/",
17436
+ ...props
17437
+ }) {
17429
17438
  const eventBus = useEventBus();
17430
- const { completeEvent, onComplete, ...rest } = props;
17439
+ const mergedProps = { effectSpriteUrl, assetBaseUrl, ...props };
17440
+ const { completeEvent, onComplete, ...rest } = mergedProps;
17431
17441
  const handleComplete = React74.useCallback(() => {
17432
17442
  if (completeEvent) eventBus.emit(`UI:${completeEvent}`, {});
17433
17443
  onComplete?.();
17434
17444
  }, [completeEvent, eventBus, onComplete]);
17435
17445
  const enhancedProps = { ...rest, onComplete: handleComplete };
17436
- if (props.assetManifest) {
17437
- return /* @__PURE__ */ jsxRuntime.jsx(CanvasEffectEngine, { ...enhancedProps, assetManifest: props.assetManifest });
17446
+ if (rest.assetManifest) {
17447
+ return /* @__PURE__ */ jsxRuntime.jsx(CanvasEffectEngine, { ...enhancedProps, assetManifest: rest.assetManifest });
17438
17448
  }
17439
17449
  return /* @__PURE__ */ jsxRuntime.jsx(EmojiEffect, { ...enhancedProps });
17440
17450
  }
@@ -18219,6 +18229,10 @@ var init_CaseStudyOrganism = __esm({
18219
18229
  });
18220
18230
  function CastleBoard({
18221
18231
  entity,
18232
+ tiles: propTiles,
18233
+ units: propUnits,
18234
+ features: propFeatures,
18235
+ assetManifest: propAssetManifest,
18222
18236
  scale = 0.45,
18223
18237
  header,
18224
18238
  sidePanel,
@@ -18234,10 +18248,10 @@ function CastleBoard({
18234
18248
  }) {
18235
18249
  const eventBus = useEventBus();
18236
18250
  const resolved = boardEntity(entity);
18237
- const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
18238
- const features = Array.isArray(resolved?.features) ? resolved.features : [];
18239
- const units = Array.isArray(resolved?.units) ? resolved.units : [];
18240
- const assetManifest = resolved?.assetManifest;
18251
+ const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
18252
+ const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
18253
+ const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
18254
+ const assetManifest = propAssetManifest ?? resolved?.assetManifest;
18241
18255
  const backgroundImage = resolved?.backgroundImage;
18242
18256
  const [hoveredTile, setHoveredTile] = React74.useState(null);
18243
18257
  const [selectedFeature, setSelectedFeature] = React74.useState(null);
@@ -18340,14 +18354,22 @@ var init_CastleBoard = __esm({
18340
18354
  function CastleTemplate({
18341
18355
  entity,
18342
18356
  scale = 0.45,
18357
+ tiles,
18358
+ units,
18359
+ features,
18360
+ assetManifest,
18343
18361
  className
18344
18362
  }) {
18345
18363
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
18346
- if (!resolved) return null;
18364
+ if (!resolved && !tiles && !units && !features && !assetManifest) return null;
18347
18365
  return /* @__PURE__ */ jsxRuntime.jsx(
18348
18366
  CastleBoard,
18349
18367
  {
18350
18368
  entity: resolved,
18369
+ tiles,
18370
+ units,
18371
+ features,
18372
+ assetManifest,
18351
18373
  scale,
18352
18374
  featureClickEvent: "FEATURE_CLICK",
18353
18375
  unitClickEvent: "UNIT_CLICK",
@@ -22335,10 +22357,10 @@ function Row({
22335
22357
  const [keyDraft, setKeyDraft] = React74__namespace.default.useState(rowKey);
22336
22358
  React74__namespace.default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
22337
22359
  const container = isObj(value) || isArr(value);
22338
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "group", children: [
22339
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", className: "py-0.5", children: [
22360
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "group w-max min-w-full", children: [
22361
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", className: "py-0.5 w-max", children: [
22340
22362
  /* @__PURE__ */ jsxRuntime.jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22341
- isArrayItem ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", className: "w-7 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-28 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
22363
+ isArrayItem ? /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "muted", className: "w-6 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
22342
22364
  exports.Input,
22343
22365
  {
22344
22366
  inputType: "text",
@@ -22351,8 +22373,7 @@ function Row({
22351
22373
  className: "font-mono"
22352
22374
  }
22353
22375
  ) }),
22354
- !container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22355
- container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
22376
+ !container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-48 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22356
22377
  /* @__PURE__ */ jsxRuntime.jsx(
22357
22378
  exports.Button,
22358
22379
  {
@@ -22361,11 +22382,11 @@ function Row({
22361
22382
  icon: "x",
22362
22383
  onClick: onRemove,
22363
22384
  "aria-label": "Remove",
22364
- className: "opacity-0 group-hover:opacity-100 transition-opacity"
22385
+ className: "shrink-0 text-muted-foreground hover:text-error"
22365
22386
  }
22366
22387
  )
22367
22388
  ] }),
22368
- container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-3", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22389
+ container && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22369
22390
  ] });
22370
22391
  }
22371
22392
  function ContainerNode({
@@ -22432,7 +22453,7 @@ function ContainerNode({
22432
22453
  exports.VStack,
22433
22454
  {
22434
22455
  gap: "none",
22435
- className: cn("ml-2 pl-2 border-l-[length:var(--border-width-thin)] border-border"),
22456
+ className: cn("ml-1.5 pl-1.5 w-max min-w-full border-l-[length:var(--border-width-thin)] border-border"),
22436
22457
  children: [
22437
22458
  entries.map(([k, v], idx) => /* @__PURE__ */ jsxRuntime.jsx(
22438
22459
  Row,
@@ -22480,15 +22501,15 @@ var init_JsonTreeEditor = __esm({
22480
22501
  TYPE_LABEL = {
22481
22502
  object: "{}",
22482
22503
  array: "[]",
22483
- string: "abc",
22484
- number: "123",
22485
- boolean: "on/off",
22504
+ string: "txt",
22505
+ number: "num",
22506
+ boolean: "y/n",
22486
22507
  null: "\u2014"
22487
22508
  };
22488
22509
  KIND_OPTIONS = ["string", "number", "boolean", "object", "array"];
22489
22510
  exports.JsonTreeEditor = ({ value, onChange, className }) => {
22490
22511
  const root = value ?? "";
22491
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22512
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full overflow-x-auto rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsxRuntime.jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22492
22513
  };
22493
22514
  }
22494
22515
  });
@@ -26235,8 +26256,8 @@ function GameCanvas2D({
26235
26256
  tickEvent,
26236
26257
  drawEvent,
26237
26258
  fps = 60,
26238
- backgroundImage,
26239
- assetBaseUrl = "",
26259
+ backgroundImage = "https://almadar-kflow-assets.web.app/shared/scenes/resonators.jpeg",
26260
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
26240
26261
  className
26241
26262
  }) {
26242
26263
  const canvasRef = React74__namespace.useRef(null);
@@ -26300,6 +26321,9 @@ function GameCanvas2D({
26300
26321
  const bgImg = loadImage(backgroundImageRef.current);
26301
26322
  if (bgImg) {
26302
26323
  ctx.drawImage(bgImg, 0, 0, widthRef.current, heightRef.current);
26324
+ } else {
26325
+ ctx.fillStyle = "#0f1420";
26326
+ ctx.fillRect(0, 0, widthRef.current, heightRef.current);
26303
26327
  }
26304
26328
  }
26305
26329
  onDrawRef.current?.(ctx, frame);
@@ -27496,10 +27520,10 @@ function PlatformerCanvas({
27496
27520
  canvasHeight = 400,
27497
27521
  followCamera = true,
27498
27522
  bgColor,
27499
- playerSprite,
27523
+ playerSprite = "https://almadar-kflow-assets.web.app/shared/platformer/characters/platformChar_idle.png",
27500
27524
  tileSprites,
27501
- backgroundImage,
27502
- assetBaseUrl = "",
27525
+ backgroundImage = "https://almadar-kflow-assets.web.app/shared/scenes/court.png",
27526
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/platformer/",
27503
27527
  leftEvent = "MOVE_LEFT",
27504
27528
  rightEvent = "MOVE_RIGHT",
27505
27529
  jumpEvent = "JUMP",
@@ -38382,7 +38406,7 @@ var init_DetailPanel = __esm({
38382
38406
  function DialogueBubble({
38383
38407
  speaker,
38384
38408
  text,
38385
- portrait = "https://almadar-kflow-assets.web.app/shared/characters/archetypes/00_base_model.png",
38409
+ portrait = "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
38386
38410
  position = "bottom",
38387
38411
  className
38388
38412
  }) {
@@ -46673,6 +46697,10 @@ function defaultIsInRange(from, to, range) {
46673
46697
  }
46674
46698
  function WorldMapBoard({
46675
46699
  entity,
46700
+ tiles: propTiles,
46701
+ units: propUnits,
46702
+ features: propFeatures,
46703
+ assetManifest: propAssetManifest,
46676
46704
  isLoading,
46677
46705
  scale = 0.4,
46678
46706
  unitScale = 2.5,
@@ -46701,16 +46729,16 @@ function WorldMapBoard({
46701
46729
  const resolved = boardEntity(entity);
46702
46730
  const hexes = rows(resolved?.hexes);
46703
46731
  const heroes = rows(resolved?.heroes);
46704
- const features = Array.isArray(resolved?.features) ? resolved.features : [];
46732
+ const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
46705
46733
  const selectedHeroId = resolved?.selectedHeroId ?? null;
46706
- const assetManifest = resolved?.assetManifest;
46734
+ const assetManifest = propAssetManifest ?? resolved?.assetManifest;
46707
46735
  const backgroundImage = resolved?.backgroundImage;
46708
46736
  const [hoveredTile, setHoveredTile] = React74.useState(null);
46709
46737
  const selectedHero = React74.useMemo(
46710
46738
  () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
46711
46739
  [heroes, selectedHeroId]
46712
46740
  );
46713
- const tiles = React74.useMemo(
46741
+ const derivedTiles = React74.useMemo(
46714
46742
  () => hexes.map((hex) => ({
46715
46743
  x: num(hex.x),
46716
46744
  y: num(hex.y),
@@ -46719,8 +46747,9 @@ function WorldMapBoard({
46719
46747
  })),
46720
46748
  [hexes]
46721
46749
  );
46750
+ const tiles = propTiles ?? derivedTiles;
46722
46751
  const baseUnits = React74.useMemo(
46723
- () => heroes.map((hero) => ({
46752
+ () => propUnits ?? heroes.map((hero) => ({
46724
46753
  id: str(hero.id),
46725
46754
  position: heroPosition(hero),
46726
46755
  name: str(hero.name),
@@ -46729,7 +46758,7 @@ function WorldMapBoard({
46729
46758
  maxHealth: 100,
46730
46759
  sprite: hero.sprite == null ? void 0 : str(hero.sprite)
46731
46760
  })),
46732
- [heroes]
46761
+ [heroes, propUnits]
46733
46762
  );
46734
46763
  const MOVE_SPEED_MS_PER_TILE = 300;
46735
46764
  const movementAnimRef = React74.useRef(null);
@@ -46939,12 +46968,20 @@ function WorldMapTemplate({
46939
46968
  unitScale = 2.5,
46940
46969
  diamondTopY,
46941
46970
  allowMoveAllHeroes = false,
46971
+ tiles,
46972
+ units,
46973
+ features,
46974
+ assetManifest,
46942
46975
  className
46943
46976
  }) {
46944
46977
  return /* @__PURE__ */ jsxRuntime.jsx(
46945
46978
  WorldMapBoard,
46946
46979
  {
46947
46980
  entity,
46981
+ tiles,
46982
+ units,
46983
+ features,
46984
+ assetManifest,
46948
46985
  scale,
46949
46986
  unitScale,
46950
46987
  diamondTopY,
@@ -9254,7 +9254,7 @@ function IsometricCanvas({
9254
9254
  // Rendering options
9255
9255
  scale = 0.4,
9256
9256
  debug: debug2 = false,
9257
- backgroundImage,
9257
+ backgroundImage = "https://almadar-kflow-assets.web.app/shared/scenes/court.png",
9258
9258
  showMinimap = true,
9259
9259
  enableCamera = true,
9260
9260
  unitScale = 1,
@@ -9269,7 +9269,7 @@ function IsometricCanvas({
9269
9269
  // Tuning
9270
9270
  diamondTopY: diamondTopYProp,
9271
9271
  // Remote asset loading
9272
- assetBaseUrl,
9272
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
9273
9273
  assetManifest
9274
9274
  }) {
9275
9275
  const tilesProp = Array.isArray(_tilesPropRaw) ? _tilesPropRaw : [];
@@ -10029,6 +10029,10 @@ var init_boardEntity = __esm({
10029
10029
  });
10030
10030
  function BattleBoard({
10031
10031
  entity,
10032
+ tiles: propTiles,
10033
+ units: propUnits,
10034
+ features: propFeatures,
10035
+ assetManifest: propAssetManifest,
10032
10036
  scale = 0.45,
10033
10037
  unitScale = 1,
10034
10038
  header,
@@ -10054,11 +10058,11 @@ function BattleBoard({
10054
10058
  className
10055
10059
  }) {
10056
10060
  const board = boardEntity(entity) ?? {};
10057
- const tiles = Array.isArray(board.tiles) ? board.tiles : [];
10058
- const features = Array.isArray(board.features) ? board.features : [];
10061
+ const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10062
+ const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
10059
10063
  const boardWidth = num(board.boardWidth, 8);
10060
10064
  const boardHeight = num(board.boardHeight, 6);
10061
- const assetManifest = board.assetManifest;
10065
+ const assetManifest = propAssetManifest ?? board.assetManifest;
10062
10066
  const backgroundImage = board.backgroundImage;
10063
10067
  const units = rows(board.units);
10064
10068
  const selectedUnitId = board.selectedUnitId ?? null;
@@ -10150,7 +10154,7 @@ function BattleBoard({
10150
10154
  }, 16);
10151
10155
  return () => clearInterval(interval);
10152
10156
  }, []);
10153
- const isoUnits = useMemo(() => {
10157
+ const derivedIsoUnits = useMemo(() => {
10154
10158
  return units.filter((u) => unitHealth(u) > 0).map((unit) => {
10155
10159
  const id = str(unit.id);
10156
10160
  const pos = movingPositions.get(id) ?? unitPosition(unit);
@@ -10174,6 +10178,7 @@ function BattleBoard({
10174
10178
  };
10175
10179
  });
10176
10180
  }, [units, movingPositions]);
10181
+ const isoUnits = propUnits ?? derivedIsoUnits;
10177
10182
  const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
10178
10183
  const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
10179
10184
  const tileToScreen = useCallback(
@@ -17377,16 +17382,21 @@ function EmojiEffect({
17377
17382
  }
17378
17383
  );
17379
17384
  }
17380
- function CanvasEffect(props) {
17385
+ function CanvasEffect({
17386
+ effectSpriteUrl = "https://almadar-kflow-assets.web.app/shared/effects/gas/gas00.png",
17387
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/effects/",
17388
+ ...props
17389
+ }) {
17381
17390
  const eventBus = useEventBus();
17382
- const { completeEvent, onComplete, ...rest } = props;
17391
+ const mergedProps = { effectSpriteUrl, assetBaseUrl, ...props };
17392
+ const { completeEvent, onComplete, ...rest } = mergedProps;
17383
17393
  const handleComplete = useCallback(() => {
17384
17394
  if (completeEvent) eventBus.emit(`UI:${completeEvent}`, {});
17385
17395
  onComplete?.();
17386
17396
  }, [completeEvent, eventBus, onComplete]);
17387
17397
  const enhancedProps = { ...rest, onComplete: handleComplete };
17388
- if (props.assetManifest) {
17389
- return /* @__PURE__ */ jsx(CanvasEffectEngine, { ...enhancedProps, assetManifest: props.assetManifest });
17398
+ if (rest.assetManifest) {
17399
+ return /* @__PURE__ */ jsx(CanvasEffectEngine, { ...enhancedProps, assetManifest: rest.assetManifest });
17390
17400
  }
17391
17401
  return /* @__PURE__ */ jsx(EmojiEffect, { ...enhancedProps });
17392
17402
  }
@@ -18171,6 +18181,10 @@ var init_CaseStudyOrganism = __esm({
18171
18181
  });
18172
18182
  function CastleBoard({
18173
18183
  entity,
18184
+ tiles: propTiles,
18185
+ units: propUnits,
18186
+ features: propFeatures,
18187
+ assetManifest: propAssetManifest,
18174
18188
  scale = 0.45,
18175
18189
  header,
18176
18190
  sidePanel,
@@ -18186,10 +18200,10 @@ function CastleBoard({
18186
18200
  }) {
18187
18201
  const eventBus = useEventBus();
18188
18202
  const resolved = boardEntity(entity);
18189
- const tiles = Array.isArray(resolved?.tiles) ? resolved.tiles : [];
18190
- const features = Array.isArray(resolved?.features) ? resolved.features : [];
18191
- const units = Array.isArray(resolved?.units) ? resolved.units : [];
18192
- const assetManifest = resolved?.assetManifest;
18203
+ const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
18204
+ const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
18205
+ const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
18206
+ const assetManifest = propAssetManifest ?? resolved?.assetManifest;
18193
18207
  const backgroundImage = resolved?.backgroundImage;
18194
18208
  const [hoveredTile, setHoveredTile] = useState(null);
18195
18209
  const [selectedFeature, setSelectedFeature] = useState(null);
@@ -18292,14 +18306,22 @@ var init_CastleBoard = __esm({
18292
18306
  function CastleTemplate({
18293
18307
  entity,
18294
18308
  scale = 0.45,
18309
+ tiles,
18310
+ units,
18311
+ features,
18312
+ assetManifest,
18295
18313
  className
18296
18314
  }) {
18297
18315
  const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
18298
- if (!resolved) return null;
18316
+ if (!resolved && !tiles && !units && !features && !assetManifest) return null;
18299
18317
  return /* @__PURE__ */ jsx(
18300
18318
  CastleBoard,
18301
18319
  {
18302
18320
  entity: resolved,
18321
+ tiles,
18322
+ units,
18323
+ features,
18324
+ assetManifest,
18303
18325
  scale,
18304
18326
  featureClickEvent: "FEATURE_CLICK",
18305
18327
  unitClickEvent: "UNIT_CLICK",
@@ -22287,10 +22309,10 @@ function Row({
22287
22309
  const [keyDraft, setKeyDraft] = React74__default.useState(rowKey);
22288
22310
  React74__default.useEffect(() => setKeyDraft(rowKey), [rowKey]);
22289
22311
  const container = isObj(value) || isArr(value);
22290
- return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "group", children: [
22291
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", className: "py-0.5", children: [
22312
+ return /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "group w-max min-w-full", children: [
22313
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", className: "py-0.5 w-max", children: [
22292
22314
  /* @__PURE__ */ jsx(KindSelect, { kind: kindOf(value), onChange: onChangeKind }),
22293
- isArrayItem ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "w-7 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsx("div", { className: "w-28 shrink-0", children: /* @__PURE__ */ jsx(
22315
+ isArrayItem ? /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "muted", className: "w-6 shrink-0 font-mono", children: rowKey }) : /* @__PURE__ */ jsx("div", { className: "w-20 shrink-0", children: /* @__PURE__ */ jsx(
22294
22316
  Input,
22295
22317
  {
22296
22318
  inputType: "text",
@@ -22303,8 +22325,7 @@ function Row({
22303
22325
  className: "font-mono"
22304
22326
  }
22305
22327
  ) }),
22306
- !container && /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22307
- container && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
22328
+ !container && /* @__PURE__ */ jsx("div", { className: "w-48 shrink-0", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) }),
22308
22329
  /* @__PURE__ */ jsx(
22309
22330
  Button,
22310
22331
  {
@@ -22313,11 +22334,11 @@ function Row({
22313
22334
  icon: "x",
22314
22335
  onClick: onRemove,
22315
22336
  "aria-label": "Remove",
22316
- className: "opacity-0 group-hover:opacity-100 transition-opacity"
22337
+ className: "shrink-0 text-muted-foreground hover:text-error"
22317
22338
  }
22318
22339
  )
22319
22340
  ] }),
22320
- container && /* @__PURE__ */ jsx("div", { className: "pl-3", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22341
+ container && /* @__PURE__ */ jsx("div", { className: "pl-2", children: /* @__PURE__ */ jsx(ValueNode, { value, onChange: onValue, depth: depth + 1 }) })
22321
22342
  ] });
22322
22343
  }
22323
22344
  function ContainerNode({
@@ -22384,7 +22405,7 @@ function ContainerNode({
22384
22405
  VStack,
22385
22406
  {
22386
22407
  gap: "none",
22387
- className: cn("ml-2 pl-2 border-l-[length:var(--border-width-thin)] border-border"),
22408
+ className: cn("ml-1.5 pl-1.5 w-max min-w-full border-l-[length:var(--border-width-thin)] border-border"),
22388
22409
  children: [
22389
22410
  entries.map(([k, v], idx) => /* @__PURE__ */ jsx(
22390
22411
  Row,
@@ -22432,15 +22453,15 @@ var init_JsonTreeEditor = __esm({
22432
22453
  TYPE_LABEL = {
22433
22454
  object: "{}",
22434
22455
  array: "[]",
22435
- string: "abc",
22436
- number: "123",
22437
- boolean: "on/off",
22456
+ string: "txt",
22457
+ number: "num",
22458
+ boolean: "y/n",
22438
22459
  null: "\u2014"
22439
22460
  };
22440
22461
  KIND_OPTIONS = ["string", "number", "boolean", "object", "array"];
22441
22462
  JsonTreeEditor = ({ value, onChange, className }) => {
22442
22463
  const root = value ?? "";
22443
- return /* @__PURE__ */ jsx("div", { className: cn("w-full rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22464
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full overflow-x-auto rounded-sm bg-card/40 p-2 border-[length:var(--border-width-thin)] border-border", className), children: /* @__PURE__ */ jsx(ValueNode, { value: root, onChange, depth: 0 }) });
22444
22465
  };
22445
22466
  }
22446
22467
  });
@@ -26187,8 +26208,8 @@ function GameCanvas2D({
26187
26208
  tickEvent,
26188
26209
  drawEvent,
26189
26210
  fps = 60,
26190
- backgroundImage,
26191
- assetBaseUrl = "",
26211
+ backgroundImage = "https://almadar-kflow-assets.web.app/shared/scenes/resonators.jpeg",
26212
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/",
26192
26213
  className
26193
26214
  }) {
26194
26215
  const canvasRef = React74.useRef(null);
@@ -26252,6 +26273,9 @@ function GameCanvas2D({
26252
26273
  const bgImg = loadImage(backgroundImageRef.current);
26253
26274
  if (bgImg) {
26254
26275
  ctx.drawImage(bgImg, 0, 0, widthRef.current, heightRef.current);
26276
+ } else {
26277
+ ctx.fillStyle = "#0f1420";
26278
+ ctx.fillRect(0, 0, widthRef.current, heightRef.current);
26255
26279
  }
26256
26280
  }
26257
26281
  onDrawRef.current?.(ctx, frame);
@@ -27448,10 +27472,10 @@ function PlatformerCanvas({
27448
27472
  canvasHeight = 400,
27449
27473
  followCamera = true,
27450
27474
  bgColor,
27451
- playerSprite,
27475
+ playerSprite = "https://almadar-kflow-assets.web.app/shared/platformer/characters/platformChar_idle.png",
27452
27476
  tileSprites,
27453
- backgroundImage,
27454
- assetBaseUrl = "",
27477
+ backgroundImage = "https://almadar-kflow-assets.web.app/shared/scenes/court.png",
27478
+ assetBaseUrl = "https://almadar-kflow-assets.web.app/shared/platformer/",
27455
27479
  leftEvent = "MOVE_LEFT",
27456
27480
  rightEvent = "MOVE_RIGHT",
27457
27481
  jumpEvent = "JUMP",
@@ -38334,7 +38358,7 @@ var init_DetailPanel = __esm({
38334
38358
  function DialogueBubble({
38335
38359
  speaker,
38336
38360
  text,
38337
- portrait = "https://almadar-kflow-assets.web.app/shared/characters/archetypes/00_base_model.png",
38361
+ portrait = "https://almadar-kflow-assets.web.app/shared/characters/archetypes/04_hero.png",
38338
38362
  position = "bottom",
38339
38363
  className
38340
38364
  }) {
@@ -46625,6 +46649,10 @@ function defaultIsInRange(from, to, range) {
46625
46649
  }
46626
46650
  function WorldMapBoard({
46627
46651
  entity,
46652
+ tiles: propTiles,
46653
+ units: propUnits,
46654
+ features: propFeatures,
46655
+ assetManifest: propAssetManifest,
46628
46656
  isLoading,
46629
46657
  scale = 0.4,
46630
46658
  unitScale = 2.5,
@@ -46653,16 +46681,16 @@ function WorldMapBoard({
46653
46681
  const resolved = boardEntity(entity);
46654
46682
  const hexes = rows(resolved?.hexes);
46655
46683
  const heroes = rows(resolved?.heroes);
46656
- const features = Array.isArray(resolved?.features) ? resolved.features : [];
46684
+ const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
46657
46685
  const selectedHeroId = resolved?.selectedHeroId ?? null;
46658
- const assetManifest = resolved?.assetManifest;
46686
+ const assetManifest = propAssetManifest ?? resolved?.assetManifest;
46659
46687
  const backgroundImage = resolved?.backgroundImage;
46660
46688
  const [hoveredTile, setHoveredTile] = useState(null);
46661
46689
  const selectedHero = useMemo(
46662
46690
  () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
46663
46691
  [heroes, selectedHeroId]
46664
46692
  );
46665
- const tiles = useMemo(
46693
+ const derivedTiles = useMemo(
46666
46694
  () => hexes.map((hex) => ({
46667
46695
  x: num(hex.x),
46668
46696
  y: num(hex.y),
@@ -46671,8 +46699,9 @@ function WorldMapBoard({
46671
46699
  })),
46672
46700
  [hexes]
46673
46701
  );
46702
+ const tiles = propTiles ?? derivedTiles;
46674
46703
  const baseUnits = useMemo(
46675
- () => heroes.map((hero) => ({
46704
+ () => propUnits ?? heroes.map((hero) => ({
46676
46705
  id: str(hero.id),
46677
46706
  position: heroPosition(hero),
46678
46707
  name: str(hero.name),
@@ -46681,7 +46710,7 @@ function WorldMapBoard({
46681
46710
  maxHealth: 100,
46682
46711
  sprite: hero.sprite == null ? void 0 : str(hero.sprite)
46683
46712
  })),
46684
- [heroes]
46713
+ [heroes, propUnits]
46685
46714
  );
46686
46715
  const MOVE_SPEED_MS_PER_TILE = 300;
46687
46716
  const movementAnimRef = useRef(null);
@@ -46891,12 +46920,20 @@ function WorldMapTemplate({
46891
46920
  unitScale = 2.5,
46892
46921
  diamondTopY,
46893
46922
  allowMoveAllHeroes = false,
46923
+ tiles,
46924
+ units,
46925
+ features,
46926
+ assetManifest,
46894
46927
  className
46895
46928
  }) {
46896
46929
  return /* @__PURE__ */ jsx(
46897
46930
  WorldMapBoard,
46898
46931
  {
46899
46932
  entity,
46933
+ tiles,
46934
+ units,
46935
+ features,
46936
+ assetManifest,
46900
46937
  scale,
46901
46938
  unitScale,
46902
46939
  diamondTopY,