@dreamboard-games/sdk 0.2.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/LICENSE.md +96 -0
- package/README.md +12 -0
- package/dist/HandView-ncJIVLhN.d.ts +193 -0
- package/dist/ResourceCounter-CTREyF73.d.ts +102 -0
- package/dist/ThemeProvider-fy0_QzgO.d.ts +99 -0
- package/dist/bundle-TIZcw8LB.d.ts +281 -0
- package/dist/cards-Sl3b40Mv.d.ts +13 -0
- package/dist/chunk-7YAHLYBR.js +481 -0
- package/dist/chunk-7YAHLYBR.js.map +1 -0
- package/dist/chunk-FDNZTDD6.js +8085 -0
- package/dist/chunk-FDNZTDD6.js.map +1 -0
- package/dist/chunk-GKKBPPSW.js +598 -0
- package/dist/chunk-GKKBPPSW.js.map +1 -0
- package/dist/chunk-I46YJSOD.js +1 -0
- package/dist/chunk-I46YJSOD.js.map +1 -0
- package/dist/chunk-KAELH4KC.js +104 -0
- package/dist/chunk-KAELH4KC.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-T3ZKNUZ7.js +1 -0
- package/dist/chunk-T3ZKNUZ7.js.map +1 -0
- package/dist/chunk-T52J5RMF.js +1 -0
- package/dist/chunk-T52J5RMF.js.map +1 -0
- package/dist/chunk-TDSWKVZ4.js +5401 -0
- package/dist/chunk-TDSWKVZ4.js.map +1 -0
- package/dist/chunk-U5C6BONG.js +34 -0
- package/dist/chunk-U5C6BONG.js.map +1 -0
- package/dist/chunk-VDXOF4FW.js +69 -0
- package/dist/chunk-VDXOF4FW.js.map +1 -0
- package/dist/chunk-VFTAA4WO.js +115 -0
- package/dist/chunk-VFTAA4WO.js.map +1 -0
- package/dist/chunk-WN74KVNY.js +17 -0
- package/dist/chunk-WN74KVNY.js.map +1 -0
- package/dist/chunk-WYPQ3GG5.js +10990 -0
- package/dist/chunk-WYPQ3GG5.js.map +1 -0
- package/dist/components-D5ZRE2Hl.d.ts +1451 -0
- package/dist/generated/runtime/primitives.d.ts +12 -0
- package/dist/generated/runtime/primitives.js +180 -0
- package/dist/generated/runtime/primitives.js.map +1 -0
- package/dist/generated/runtime-api.d.ts +3 -0
- package/dist/generated/runtime-api.js +2 -0
- package/dist/generated/runtime-api.js.map +1 -0
- package/dist/generated/runtime.d.ts +14 -0
- package/dist/generated/runtime.js +18 -0
- package/dist/generated/runtime.js.map +1 -0
- package/dist/generated/workspace-contract.d.ts +14 -0
- package/dist/generated/workspace-contract.js +14 -0
- package/dist/generated/workspace-contract.js.map +1 -0
- package/dist/hex-board-view-D_07hO6O.d.ts +933 -0
- package/dist/hex-color-MhOyuY-o.d.ts +8 -0
- package/dist/index-BwqPQtBu.d.ts +1433 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/reducer-bundle-abi.d.ts +1083 -0
- package/dist/infrastructure/reducer-bundle-abi.js +14 -0
- package/dist/infrastructure/reducer-bundle-abi.js.map +1 -0
- package/dist/infrastructure/workspace-codegen.d.ts +53 -0
- package/dist/infrastructure/workspace-codegen.js +44 -0
- package/dist/infrastructure/workspace-codegen.js.map +1 -0
- package/dist/manifest-contract-BNHVGFtU.d.ts +9 -0
- package/dist/package-set.d.ts +13 -0
- package/dist/package-set.js +12 -0
- package/dist/package-set.js.map +1 -0
- package/dist/primitive-props-DpKs-GCr.d.ts +11 -0
- package/dist/reducer.d.ts +3786 -0
- package/dist/reducer.js +8131 -0
- package/dist/reducer.js.map +1 -0
- package/dist/runtime/primitives.d.ts +226 -0
- package/dist/runtime/primitives.js +180 -0
- package/dist/runtime/primitives.js.map +1 -0
- package/dist/runtime/types/runtime-api.d.ts +1 -0
- package/dist/runtime/types/runtime-api.js +2 -0
- package/dist/runtime/types/runtime-api.js.map +1 -0
- package/dist/runtime/workspace-contract.d.ts +172 -0
- package/dist/runtime/workspace-contract.js +14 -0
- package/dist/runtime/workspace-contract.js.map +1 -0
- package/dist/runtime-api-3dshj6kK.d.ts +101 -0
- package/dist/runtime-api-DWxvTr-O.d.ts +379 -0
- package/dist/runtime.d.ts +58 -0
- package/dist/runtime.js +13 -0
- package/dist/runtime.js.map +1 -0
- package/dist/slots-1GPGihk8.d.ts +8 -0
- package/dist/testing.d.ts +149 -0
- package/dist/testing.js +513 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +496 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/components.d.ts +16 -0
- package/dist/ui/components.js +192 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/defaults.d.ts +19 -0
- package/dist/ui/defaults.js +104 -0
- package/dist/ui/defaults.js.map +1 -0
- package/dist/ui/plugin-styles.css +250 -0
- package/dist/ui/types/player-state.d.ts +365 -0
- package/dist/ui/types/player-state.js +1 -0
- package/dist/ui/types/player-state.js.map +1 -0
- package/dist/ui-contract-iQfTtUSL.d.ts +1161 -0
- package/dist/ui.d.ts +320 -0
- package/dist/ui.js +253 -0
- package/dist/ui.js.map +1 -0
- package/package.json +199 -0
- package/src/generated/reducer-contract/builders.ts +90 -0
- package/src/generated/reducer-contract/version.ts +9 -0
- package/src/generated/reducer-contract/wire.ts +100 -0
- package/src/generated/reducer-contract/zod.ts +101 -0
- package/src/generated/runtime/primitives.ts +2 -0
- package/src/generated/runtime-api.ts +5 -0
- package/src/generated/runtime.ts +35 -0
- package/src/generated/workspace-contract.ts +2 -0
- package/src/index.ts +7 -0
- package/src/infrastructure/reducer-bundle-abi.ts +8 -0
- package/src/infrastructure/reducer-contract/bundle.ts +37 -0
- package/src/infrastructure/workspace-codegen/hex-geometry.ts +69 -0
- package/src/infrastructure/workspace-codegen/index.ts +64 -0
- package/src/infrastructure/workspace-codegen/manifest-contract.ts +6632 -0
- package/src/infrastructure/workspace-codegen/manifest-validation.ts +795 -0
- package/src/infrastructure/workspace-codegen/ownership.ts +131 -0
- package/src/infrastructure/workspace-codegen/preset-card-sets.ts +169 -0
- package/src/infrastructure/workspace-codegen/seeds.ts +1705 -0
- package/src/infrastructure/workspace-codegen.ts +1 -0
- package/src/package-set.ts +19 -0
- package/src/reducer/authoring/contract.ts +157 -0
- package/src/reducer/authoring/effect.ts +224 -0
- package/src/reducer/authoring/game.ts +23 -0
- package/src/reducer/authoring/interaction.ts +98 -0
- package/src/reducer/authoring/phase.ts +300 -0
- package/src/reducer/authoring/types.ts +70 -0
- package/src/reducer/authoring/validation.ts +382 -0
- package/src/reducer/authoring/view-stage.ts +68 -0
- package/src/reducer/authoring.ts +29 -0
- package/src/reducer/bundle/ingress-bundle.ts +491 -0
- package/src/reducer/bundle/trusted/engine-instruction-resolver.ts +254 -0
- package/src/reducer/bundle/trusted/flow-instruction-resolver.ts +73 -0
- package/src/reducer/bundle/trusted/instruction-runner.ts +414 -0
- package/src/reducer/bundle/trusted/interaction-authorization.ts +137 -0
- package/src/reducer/bundle/trusted/interaction-collectors.ts +859 -0
- package/src/reducer/bundle/trusted/interaction-decision.ts +747 -0
- package/src/reducer/bundle/trusted/interaction-resolver.ts +95 -0
- package/src/reducer/bundle/trusted/interaction-types.ts +171 -0
- package/src/reducer/bundle/trusted/lifecycle-runner.ts +427 -0
- package/src/reducer/bundle/trusted/projection-builder.ts +356 -0
- package/src/reducer/bundle/trusted/projection-context.ts +39 -0
- package/src/reducer/bundle/trusted/rng-sampler.ts +150 -0
- package/src/reducer/bundle/trusted/runtime-registry.ts +120 -0
- package/src/reducer/bundle/trusted/runtime-scope.ts +336 -0
- package/src/reducer/bundle/trusted/simultaneous-player.ts +97 -0
- package/src/reducer/bundle/trusted/stage-resolver.ts +87 -0
- package/src/reducer/bundle/trusted/static-projection.ts +116 -0
- package/src/reducer/bundle/trusted/trusted-runtime-args.ts +97 -0
- package/src/reducer/bundle/trusted/trusted-runtime-result.ts +39 -0
- package/src/reducer/bundle/trusted/trusted-setup-profiles.ts +43 -0
- package/src/reducer/bundle/trusted/trusted-state-codec.ts +48 -0
- package/src/reducer/bundle/trusted-bundle.ts +97 -0
- package/src/reducer/bundle/types.ts +171 -0
- package/src/reducer/bundle.ts +2 -0
- package/src/reducer/client-param-schemas.ts +57 -0
- package/src/reducer/compose.ts +34 -0
- package/src/reducer/core/runtime-input.ts +30 -0
- package/src/reducer/core/runtime-instruction.ts +59 -0
- package/src/reducer/core/types.ts +62 -0
- package/src/reducer/definition-index.ts +277 -0
- package/src/reducer/derived.ts +106 -0
- package/src/reducer/effects.ts +92 -0
- package/src/reducer/engine/runtime-instruction-engine.ts +155 -0
- package/src/reducer/ingress/decode-runtime-input.ts +7 -0
- package/src/reducer/ingress/decode-session-state.ts +9 -0
- package/src/reducer/ingress/encode-session-state.ts +6 -0
- package/src/reducer/ingress/input-codec.ts +18 -0
- package/src/reducer/ingress/phase-schemas.ts +62 -0
- package/src/reducer/ingress/raw-types.ts +107 -0
- package/src/reducer/ingress/runtime-codec.ts +14 -0
- package/src/reducer/ingress/runtime-payload.ts +13 -0
- package/src/reducer/ingress/session-codec.ts +392 -0
- package/src/reducer/ingress/types.ts +6 -0
- package/src/reducer/inputs/boardInput.ts +217 -0
- package/src/reducer/inputs/boardTarget.ts +190 -0
- package/src/reducer/inputs/cardInput.ts +86 -0
- package/src/reducer/inputs/cardTarget.ts +101 -0
- package/src/reducer/inputs/choiceTarget.ts +104 -0
- package/src/reducer/inputs/defineInputs.ts +71 -0
- package/src/reducer/inputs/formInput.ts +809 -0
- package/src/reducer/inputs/many.ts +120 -0
- package/src/reducer/inputs/promptInput.ts +87 -0
- package/src/reducer/inputs/rngInput.ts +58 -0
- package/src/reducer/inputs/targetRule.ts +123 -0
- package/src/reducer/inputs.ts +41 -0
- package/src/reducer/model/definition.ts +1072 -0
- package/src/reducer/model/extract.ts +745 -0
- package/src/reducer/model/manifest.ts +570 -0
- package/src/reducer/model/queries.ts +641 -0
- package/src/reducer/model/runtime.ts +264 -0
- package/src/reducer/model/spec.ts +1386 -0
- package/src/reducer/model/table.ts +260 -0
- package/src/reducer/model.ts +7 -0
- package/src/reducer/ops.ts +1034 -0
- package/src/reducer/parse-utils.ts +28 -0
- package/src/reducer/per-player.ts +422 -0
- package/src/reducer/rng.ts +69 -0
- package/src/reducer/schema-helpers.ts +185 -0
- package/src/reducer/setup-bootstrap-helpers.ts +171 -0
- package/src/reducer/setup-bootstrap.ts +481 -0
- package/src/reducer/table-ops.ts +2671 -0
- package/src/reducer/table-queries.ts +372 -0
- package/src/reducer/transaction.ts +120 -0
- package/src/reducer.ts +314 -0
- package/src/runtime/primitives.ts +1 -0
- package/src/runtime/types/runtime-api.ts +1 -0
- package/src/runtime/workspace-contract.ts +32 -0
- package/src/runtime-internal/components/InteractionForm.tsx +1309 -0
- package/src/runtime-internal/components/PluginRuntime.tsx +103 -0
- package/src/runtime-internal/components/board/target-layer.ts +70 -0
- package/src/runtime-internal/context/ClientParamSchemaContext.tsx +44 -0
- package/src/runtime-internal/context/InteractionDraftContext.tsx +279 -0
- package/src/runtime-internal/context/PluginSessionContext.tsx +47 -0
- package/src/runtime-internal/context/PluginStateContext.tsx +262 -0
- package/src/runtime-internal/context/RuntimeContext.tsx +96 -0
- package/src/runtime-internal/defaults/components.tsx +409 -0
- package/src/runtime-internal/defaults/index.ts +11 -0
- package/src/runtime-internal/errors/ValidationError.ts +29 -0
- package/src/runtime-internal/hooks/useActivePlayers.ts +33 -0
- package/src/runtime-internal/hooks/useBoardInteractions.ts +665 -0
- package/src/runtime-internal/hooks/useGameSelector.ts +105 -0
- package/src/runtime-internal/hooks/useGameView.ts +9 -0
- package/src/runtime-internal/hooks/useInteractionByKey.ts +354 -0
- package/src/runtime-internal/hooks/useInteractionHandle.ts +438 -0
- package/src/runtime-internal/hooks/useIsMyTurn.ts +20 -0
- package/src/runtime-internal/hooks/useLobby.ts +76 -0
- package/src/runtime-internal/hooks/useMe.ts +48 -0
- package/src/runtime-internal/hooks/usePlayerInfo.ts +28 -0
- package/src/runtime-internal/hooks/usePlayerTurnOrder.ts +23 -0
- package/src/runtime-internal/hooks/usePluginRuntime.ts +147 -0
- package/src/runtime-internal/hooks/useSeatInbox.ts +61 -0
- package/src/runtime-internal/hooks/useSimultaneousPhase.ts +10 -0
- package/src/runtime-internal/index.ts +42 -0
- package/src/runtime-internal/internal.ts +43 -0
- package/src/runtime-internal/plugin-styles.css +250 -0
- package/src/runtime-internal/primitives/board.tsx +459 -0
- package/src/runtime-internal/primitives/dialog-lifecycle.ts +58 -0
- package/src/runtime-internal/primitives/dice.tsx +79 -0
- package/src/runtime-internal/primitives/game-ui-provider.tsx +35 -0
- package/src/runtime-internal/primitives/game.tsx +387 -0
- package/src/runtime-internal/primitives/hand-intent-adapter.ts +147 -0
- package/src/runtime-internal/primitives/hand-surface.tsx +594 -0
- package/src/runtime-internal/primitives/index.ts +196 -0
- package/src/runtime-internal/primitives/interaction-form-binding.tsx +56 -0
- package/src/runtime-internal/primitives/interaction-submit.ts +90 -0
- package/src/runtime-internal/primitives/interaction.tsx +987 -0
- package/src/runtime-internal/primitives/phase.tsx +43 -0
- package/src/runtime-internal/primitives/player-roster.tsx +302 -0
- package/src/runtime-internal/primitives/primitive-props.tsx +101 -0
- package/src/runtime-internal/primitives/prompt.tsx +255 -0
- package/src/runtime-internal/primitives/ui.tsx +60 -0
- package/src/runtime-internal/primitives/zone.tsx +791 -0
- package/src/runtime-internal/reducer.ts +30 -0
- package/src/runtime-internal/runtime/createPluginRuntimeAPI.ts +605 -0
- package/src/runtime-internal/types/plugin-state.ts +508 -0
- package/src/runtime-internal/types/reducer-state.ts +24 -0
- package/src/runtime-internal/types/runtime-api.ts +114 -0
- package/src/runtime-internal/ui-contract.ts +519 -0
- package/src/runtime-internal/utils/card-intent-adapter.ts +546 -0
- package/src/runtime-internal/utils/interaction-inputs.ts +492 -0
- package/src/runtime-internal/utils/interaction-labels.ts +23 -0
- package/src/runtime-internal/utils/interaction-router.ts +273 -0
- package/src/runtime-internal/utils/interaction-status.ts +74 -0
- package/src/runtime-internal/workspace-contract.ts +1170 -0
- package/src/runtime.ts +34 -0
- package/src/testing/create-expect-api.ts +352 -0
- package/src/testing/create-test-runtime.ts +381 -0
- package/src/testing/definitions.ts +127 -0
- package/src/testing/index.ts +3 -0
- package/src/testing.ts +1 -0
- package/src/type-stubs/manifest-contract.d.ts +42 -0
- package/src/type-stubs/manifest-contract.js +72 -0
- package/src/type-stubs/ui-contract.d.ts +5 -0
- package/src/type-stubs/ui-contract.js +1 -0
- package/src/types/authoring-card-properties.type-test.ts +266 -0
- package/src/types/authoring.ts +1282 -0
- package/src/types/cards.ts +19 -0
- package/src/types/contracts.ts +1550 -0
- package/src/types/generated-helpers.ts +35 -0
- package/src/types/index.ts +147 -0
- package/src/types/slots.ts +11 -0
- package/src/types.ts +1 -0
- package/src/ui/components/ActionButton.tsx +97 -0
- package/src/ui/components/ActionPanel.tsx +315 -0
- package/src/ui/components/Card.tsx +378 -0
- package/src/ui/components/CardDragSurface.tsx +1076 -0
- package/src/ui/components/ChromeSuppressionContext.tsx +70 -0
- package/src/ui/components/CostDisplay.tsx +145 -0
- package/src/ui/components/DiceRoller.tsx +581 -0
- package/src/ui/components/Drawer.tsx +180 -0
- package/src/ui/components/ErrorBoundary.tsx +275 -0
- package/src/ui/components/GameEndDisplay.tsx +398 -0
- package/src/ui/components/GameSkeleton.tsx +260 -0
- package/src/ui/components/Hand.tsx +468 -0
- package/src/ui/components/HandDock.tsx +299 -0
- package/src/ui/components/HandView.tsx +441 -0
- package/src/ui/components/MobileHandTray.tsx +381 -0
- package/src/ui/components/MoreActions.tsx +143 -0
- package/src/ui/components/PhaseIndicator.tsx +341 -0
- package/src/ui/components/PlayArea.tsx +146 -0
- package/src/ui/components/PrimaryActionButton.tsx +336 -0
- package/src/ui/components/PrimaryButton.tsx +45 -0
- package/src/ui/components/ResourceCounter.tsx +270 -0
- package/src/ui/components/StagingZone.tsx +134 -0
- package/src/ui/components/ThemedButton.tsx +113 -0
- package/src/ui/components/Toast.tsx +264 -0
- package/src/ui/components/board/HexGrid.tsx +1294 -0
- package/src/ui/components/board/NetworkGraph.tsx +476 -0
- package/src/ui/components/board/SlotSystem.tsx +388 -0
- package/src/ui/components/board/SquareGrid.tsx +1165 -0
- package/src/ui/components/board/TrackBoard.tsx +496 -0
- package/src/ui/components/board/ZoneMap.tsx +448 -0
- package/src/ui/components/board/hex-board-view.ts +123 -0
- package/src/ui/components/board/index.ts +142 -0
- package/src/ui/components/board/interaction-accessibility.ts +21 -0
- package/src/ui/components/board/target-layer.ts +66 -0
- package/src/ui/components/card-render-content.type-test.ts +27 -0
- package/src/ui/components/hand-layout-math.ts +163 -0
- package/src/ui/components/hand-pointer-engine.ts +413 -0
- package/src/ui/components/index.ts +245 -0
- package/src/ui/components.ts +1 -0
- package/src/ui/defaults/components.tsx +106 -0
- package/src/ui/defaults/index.ts +8 -0
- package/src/ui/defaults.ts +1 -0
- package/src/ui/errors/ValidationError.ts +29 -0
- package/src/ui/helpers/cards.ts +19 -0
- package/src/ui/helpers/track-board.ts +211 -0
- package/src/ui/hooks/useBoardTopology.ts +316 -0
- package/src/ui/hooks/useCards.ts +10 -0
- package/src/ui/hooks/useHandCardPointer.ts +381 -0
- package/src/ui/hooks/useHandLayout.ts +378 -0
- package/src/ui/hooks/useHandPresentation.ts +121 -0
- package/src/ui/hooks/useHexBoard.ts +74 -0
- package/src/ui/hooks/useHexGrid.ts +185 -0
- package/src/ui/hooks/useIsMobile.ts +35 -0
- package/src/ui/hooks/usePanZoom.ts +278 -0
- package/src/ui/hooks/useSquareBoard.ts +124 -0
- package/src/ui/hooks/useSquareGrid.ts +328 -0
- package/src/ui/index.ts +98 -0
- package/src/ui/internal/ui/alert.tsx +51 -0
- package/src/ui/internal/ui/button.tsx +58 -0
- package/src/ui/internal/ui/dialog.tsx +134 -0
- package/src/ui/internal/ui/input.tsx +21 -0
- package/src/ui/internal/ui/label.tsx +21 -0
- package/src/ui/internal/ui/select.tsx +129 -0
- package/src/ui/internal/ui/tooltip.tsx +54 -0
- package/src/ui/internal/ui/utils.ts +5 -0
- package/src/ui/plugin-styles.css +250 -0
- package/src/ui/primitives/dialog-lifecycle.ts +58 -0
- package/src/ui/primitives/dice.tsx +79 -0
- package/src/ui/primitives/primitive-props.tsx +101 -0
- package/src/ui/theme/ThemeProvider.tsx +252 -0
- package/src/ui/theme/board.ts +61 -0
- package/src/ui/theme/css-vars.ts +105 -0
- package/src/ui/theme/derive.ts +240 -0
- package/src/ui/theme/index.ts +61 -0
- package/src/ui/theme/presets/arcade.ts +261 -0
- package/src/ui/theme/presets/studio.ts +261 -0
- package/src/ui/theme/presets/tabletop.ts +266 -0
- package/src/ui/theme/tokens.ts +392 -0
- package/src/ui/types/hex-color.ts +20 -0
- package/src/ui/types/player-state.ts +463 -0
- package/src/ui/types/tiled-board.ts +785 -0
- package/src/ui/types/visual-state.ts +137 -0
- package/src/ui.ts +1 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import type { InteractionUiStore } from "../context/InteractionDraftContext.js";
|
|
2
|
+
import type {
|
|
3
|
+
InteractionDescriptor,
|
|
4
|
+
PluginStateSnapshot,
|
|
5
|
+
ZoneHandlesSnapshot,
|
|
6
|
+
} from "../types/plugin-state.js";
|
|
7
|
+
import {
|
|
8
|
+
inputByKey,
|
|
9
|
+
inputByTarget,
|
|
10
|
+
isResolvedTargetDomain,
|
|
11
|
+
isTargetDomain,
|
|
12
|
+
} from "./interaction-inputs.js";
|
|
13
|
+
import {
|
|
14
|
+
claimInteractionSubmit,
|
|
15
|
+
clearInteractionRoute,
|
|
16
|
+
markInteractionPending,
|
|
17
|
+
routeCardInputIntent,
|
|
18
|
+
shouldRouteInteractionPending,
|
|
19
|
+
type InteractionDraftReadiness,
|
|
20
|
+
type RoutedInteractionTargetResult,
|
|
21
|
+
} from "./interaction-router.js";
|
|
22
|
+
import { isInteractionAvailable } from "./interaction-status.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Opaque drop-target identifier emitted by SDK presentation. The runtime
|
|
26
|
+
* adapter resolves it back into a typed `(inputKey, value)` pair against the
|
|
27
|
+
* descriptor of the card the drop is for.
|
|
28
|
+
*
|
|
29
|
+
* Two encodings are supported, both opaque to SDK presentation:
|
|
30
|
+
*
|
|
31
|
+
* - `dreamboard:drop:input:<inputKey>:<value>` — explicit collector input
|
|
32
|
+
* key. Used by callers (CLI/scaffold tests, lower-level adapters) that
|
|
33
|
+
* already know the descriptor input shape.
|
|
34
|
+
* - `dreamboard:drop:kind:<targetKind>:<value>` — typed kind reference.
|
|
35
|
+
* Used by the generated hand surface, where authors describe a target by
|
|
36
|
+
* its domain kind (`card` for staging, `space`/`edge`/`vertex`/`tile`
|
|
37
|
+
* for board destinations). The runtime adapter resolves it to the
|
|
38
|
+
* matching collector input on the live descriptor at drop time.
|
|
39
|
+
*/
|
|
40
|
+
export type RuntimeDropTargetId = string;
|
|
41
|
+
|
|
42
|
+
const DROP_TARGET_PREFIX = "dreamboard:drop";
|
|
43
|
+
|
|
44
|
+
export type RuntimeDropTargetKind =
|
|
45
|
+
| "card"
|
|
46
|
+
| "space"
|
|
47
|
+
| "edge"
|
|
48
|
+
| "vertex"
|
|
49
|
+
| "tile";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build an opaque drop-target id keyed by the descriptor input name. Used
|
|
53
|
+
* when the caller already knows the collector input shape.
|
|
54
|
+
*/
|
|
55
|
+
export function encodeRuntimeDropTargetId(
|
|
56
|
+
inputKey: string,
|
|
57
|
+
value: string,
|
|
58
|
+
): RuntimeDropTargetId {
|
|
59
|
+
return `${DROP_TARGET_PREFIX}:input:${inputKey}:${value}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build an opaque drop-target id keyed by typed target kind. Used by the
|
|
64
|
+
* generated hand surface, where authors do not (and should not) know the
|
|
65
|
+
* collector input names.
|
|
66
|
+
*/
|
|
67
|
+
export function encodeRuntimeDropTargetKind(
|
|
68
|
+
kind: RuntimeDropTargetKind,
|
|
69
|
+
value: string,
|
|
70
|
+
): RuntimeDropTargetId {
|
|
71
|
+
return `${DROP_TARGET_PREFIX}:kind:${kind}:${value}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type DecodedRuntimeDropTarget =
|
|
75
|
+
| { mode: "input"; inputKey: string; value: string }
|
|
76
|
+
| { mode: "kind"; kind: RuntimeDropTargetKind; value: string };
|
|
77
|
+
|
|
78
|
+
export function decodeRuntimeDropTargetId(
|
|
79
|
+
targetId: RuntimeDropTargetId,
|
|
80
|
+
): DecodedRuntimeDropTarget | null {
|
|
81
|
+
if (!targetId.startsWith(`${DROP_TARGET_PREFIX}:`)) return null;
|
|
82
|
+
const rest = targetId.slice(DROP_TARGET_PREFIX.length + 1);
|
|
83
|
+
if (rest.startsWith("input:")) {
|
|
84
|
+
const body = rest.slice("input:".length);
|
|
85
|
+
const sep = body.indexOf(":");
|
|
86
|
+
if (sep === -1) return null;
|
|
87
|
+
return {
|
|
88
|
+
mode: "input",
|
|
89
|
+
inputKey: body.slice(0, sep),
|
|
90
|
+
value: body.slice(sep + 1),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (rest.startsWith("kind:")) {
|
|
94
|
+
const body = rest.slice("kind:".length);
|
|
95
|
+
const sep = body.indexOf(":");
|
|
96
|
+
if (sep === -1) return null;
|
|
97
|
+
const kind = body.slice(0, sep) as RuntimeDropTargetKind;
|
|
98
|
+
const value = body.slice(sep + 1);
|
|
99
|
+
if (
|
|
100
|
+
kind === "card" ||
|
|
101
|
+
kind === "space" ||
|
|
102
|
+
kind === "edge" ||
|
|
103
|
+
kind === "vertex" ||
|
|
104
|
+
kind === "tile"
|
|
105
|
+
) {
|
|
106
|
+
return { mode: "kind", kind, value };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
// Legacy format, kept for any external callers still using
|
|
111
|
+
// `dreamboard:drop:<inputKey>:<value>`. New writers should use one of the
|
|
112
|
+
// namespaced encoders above.
|
|
113
|
+
const sep = rest.indexOf(":");
|
|
114
|
+
if (sep === -1) return null;
|
|
115
|
+
return {
|
|
116
|
+
mode: "input",
|
|
117
|
+
inputKey: rest.slice(0, sep),
|
|
118
|
+
value: rest.slice(sep + 1),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface CardIntentAdapterContext {
|
|
123
|
+
store: InteractionUiStore;
|
|
124
|
+
/** Project-side snapshot of all available interactions (gameplay slice). */
|
|
125
|
+
availableInteractions: readonly InteractionDescriptor[];
|
|
126
|
+
/** Zone snapshot for the originating hand. */
|
|
127
|
+
zoneSnapshot: ZoneHandlesSnapshot | null;
|
|
128
|
+
/** Submit through the canonical runtime path. */
|
|
129
|
+
submit: (
|
|
130
|
+
descriptor: InteractionDescriptor,
|
|
131
|
+
params: Record<string, unknown>,
|
|
132
|
+
) => Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Optional explicit interaction key. When provided, the adapter only routes
|
|
135
|
+
* intents through this interaction; otherwise it picks the first interaction
|
|
136
|
+
* eligible for the card.
|
|
137
|
+
*/
|
|
138
|
+
interactionKey?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export type CardIntentInput =
|
|
142
|
+
| { type: "activate"; cardId: string }
|
|
143
|
+
| { type: "drop"; cardId: string; targetId: string };
|
|
144
|
+
|
|
145
|
+
export type CardIntentResult =
|
|
146
|
+
| { status: "ignored"; reason: CardIntentIgnoredReason }
|
|
147
|
+
| {
|
|
148
|
+
status: "pending";
|
|
149
|
+
descriptor: InteractionDescriptor;
|
|
150
|
+
readiness: InteractionDraftReadiness;
|
|
151
|
+
params: Record<string, unknown>;
|
|
152
|
+
}
|
|
153
|
+
| {
|
|
154
|
+
status: "submitted";
|
|
155
|
+
descriptor: InteractionDescriptor;
|
|
156
|
+
readiness: InteractionDraftReadiness;
|
|
157
|
+
params: Record<string, unknown>;
|
|
158
|
+
}
|
|
159
|
+
| {
|
|
160
|
+
status: "submitting";
|
|
161
|
+
descriptor: InteractionDescriptor;
|
|
162
|
+
readiness: InteractionDraftReadiness;
|
|
163
|
+
params: Record<string, unknown>;
|
|
164
|
+
}
|
|
165
|
+
| {
|
|
166
|
+
status: "error";
|
|
167
|
+
descriptor: InteractionDescriptor;
|
|
168
|
+
error: unknown;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export type CardIntentIgnoredReason =
|
|
172
|
+
| "no-descriptor"
|
|
173
|
+
| "interaction-unavailable"
|
|
174
|
+
| "no-card-input"
|
|
175
|
+
| "card-not-eligible"
|
|
176
|
+
| "drop-target-not-decodable"
|
|
177
|
+
| "drop-target-input-unknown"
|
|
178
|
+
| "drop-target-not-eligible"
|
|
179
|
+
| "ambiguous-drop"
|
|
180
|
+
| "already-submitting";
|
|
181
|
+
|
|
182
|
+
interface CardRouteCandidate {
|
|
183
|
+
descriptor: InteractionDescriptor;
|
|
184
|
+
cardInputKey: string;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Enumerate descriptors that can accept this card. Used both to pick a
|
|
189
|
+
* single-action route for `activate` intents and to filter joint
|
|
190
|
+
* `(card, destination)` matches for `drop` intents.
|
|
191
|
+
*/
|
|
192
|
+
function listCardRouteCandidates(
|
|
193
|
+
ctx: CardIntentAdapterContext,
|
|
194
|
+
cardId: string,
|
|
195
|
+
): CardRouteCandidate[] {
|
|
196
|
+
const explicitKey = ctx.interactionKey;
|
|
197
|
+
const candidates = ctx.zoneSnapshot
|
|
198
|
+
? (ctx.zoneSnapshot.playableByCardId[cardId] ?? [])
|
|
199
|
+
: ctx.availableInteractions.filter(
|
|
200
|
+
(descriptor) =>
|
|
201
|
+
!explicitKey ||
|
|
202
|
+
descriptor.interactionKey === explicitKey ||
|
|
203
|
+
descriptor.interactionId === explicitKey,
|
|
204
|
+
);
|
|
205
|
+
const result: CardRouteCandidate[] = [];
|
|
206
|
+
for (const descriptor of candidates) {
|
|
207
|
+
if (explicitKey && descriptor.interactionKey !== explicitKey) continue;
|
|
208
|
+
if (!isInteractionAvailable(descriptor)) continue;
|
|
209
|
+
const targetInput = inputByTarget(descriptor, "card", cardId);
|
|
210
|
+
if (targetInput) {
|
|
211
|
+
result.push({ descriptor, cardInputKey: targetInput.key });
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const fallback = descriptor.inputs.find((input) => input.key === "cardId");
|
|
215
|
+
if (fallback && isTargetDomain(fallback.domain)) {
|
|
216
|
+
result.push({ descriptor, cardInputKey: fallback.key });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Apply an SDK `CardIntent` (generic activate/drop) to canonical Dreamboard
|
|
224
|
+
* collector state. Returns the runtime classification for the call site to
|
|
225
|
+
* react to (pending vs submitted vs ignored).
|
|
226
|
+
*
|
|
227
|
+
* Behavior:
|
|
228
|
+
* - `activate` is mapped onto `routeCardInputIntent` with no destination.
|
|
229
|
+
* - `drop` decodes the opaque target id; when it points at a typed input on
|
|
230
|
+
* the same descriptor, both the card and the destination are written
|
|
231
|
+
* atomically before readiness/`autoWhenReady` is evaluated.
|
|
232
|
+
* - Disabled, unavailable or ineligible intents return `ignored` even if the
|
|
233
|
+
* caller emits them.
|
|
234
|
+
*/
|
|
235
|
+
export async function applyCardIntent(
|
|
236
|
+
ctx: CardIntentAdapterContext,
|
|
237
|
+
intent: CardIntentInput,
|
|
238
|
+
): Promise<CardIntentResult> {
|
|
239
|
+
const candidates = listCardRouteCandidates(ctx, intent.cardId);
|
|
240
|
+
if (candidates.length === 0) {
|
|
241
|
+
return { status: "ignored", reason: "no-descriptor" };
|
|
242
|
+
}
|
|
243
|
+
// Filter out candidates whose card input rejects this card. The card
|
|
244
|
+
// domain may be unresolved (lazy) — those still pass since their
|
|
245
|
+
// eligibility is computed from the draft itself.
|
|
246
|
+
const cardEligible = candidates.filter(({ descriptor, cardInputKey }) => {
|
|
247
|
+
const cardInput = inputByKey(descriptor, cardInputKey);
|
|
248
|
+
if (!cardInput) return false;
|
|
249
|
+
if (!isResolvedTargetDomain(cardInput.domain)) return true;
|
|
250
|
+
return cardInput.domain.eligibleTargets.includes(intent.cardId);
|
|
251
|
+
});
|
|
252
|
+
if (cardEligible.length === 0) {
|
|
253
|
+
return { status: "ignored", reason: "card-not-eligible" };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let descriptor: InteractionDescriptor;
|
|
257
|
+
let cardInputKey: string;
|
|
258
|
+
let dropTarget: { inputKey: string; value: string } | undefined;
|
|
259
|
+
|
|
260
|
+
if (intent.type === "drop") {
|
|
261
|
+
const decoded = decodeRuntimeDropTargetId(intent.targetId);
|
|
262
|
+
if (!decoded) {
|
|
263
|
+
return { status: "ignored", reason: "drop-target-not-decodable" };
|
|
264
|
+
}
|
|
265
|
+
// Score every card-eligible candidate against the decoded destination.
|
|
266
|
+
// A descriptor is a "match" only when it accepts both the card and the
|
|
267
|
+
// dropped target; ambiguity is reported explicitly so authors can fix
|
|
268
|
+
// the underlying descriptor projection rather than getting silent
|
|
269
|
+
// submissions of the wrong action.
|
|
270
|
+
const matches = cardEligible.flatMap((candidate) => {
|
|
271
|
+
const resolvedInputKey =
|
|
272
|
+
decoded.mode === "input"
|
|
273
|
+
? decoded.inputKey
|
|
274
|
+
: resolveDropTargetInputKey(
|
|
275
|
+
candidate.descriptor,
|
|
276
|
+
decoded.kind,
|
|
277
|
+
decoded.value,
|
|
278
|
+
);
|
|
279
|
+
if (!resolvedInputKey) return [];
|
|
280
|
+
const dropInput = inputByKey(candidate.descriptor, resolvedInputKey);
|
|
281
|
+
if (!dropInput) return [];
|
|
282
|
+
if (
|
|
283
|
+
isResolvedTargetDomain(dropInput.domain) &&
|
|
284
|
+
!dropInput.domain.eligibleTargets.includes(decoded.value)
|
|
285
|
+
) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
return [
|
|
289
|
+
{
|
|
290
|
+
...candidate,
|
|
291
|
+
dropTarget: { inputKey: resolvedInputKey, value: decoded.value },
|
|
292
|
+
},
|
|
293
|
+
];
|
|
294
|
+
});
|
|
295
|
+
if (matches.length === 0) {
|
|
296
|
+
// Distinguish "no descriptor declares this input/kind at all" from
|
|
297
|
+
// "the dropped value is not eligible for any descriptor that does".
|
|
298
|
+
const anyKnowsKey = cardEligible.some((candidate) => {
|
|
299
|
+
if (decoded.mode === "input") {
|
|
300
|
+
return (
|
|
301
|
+
inputByKey(candidate.descriptor, decoded.inputKey) !== undefined
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
return (
|
|
305
|
+
resolveDropTargetInputKey(
|
|
306
|
+
candidate.descriptor,
|
|
307
|
+
decoded.kind,
|
|
308
|
+
decoded.value,
|
|
309
|
+
) !== null
|
|
310
|
+
);
|
|
311
|
+
});
|
|
312
|
+
return {
|
|
313
|
+
status: "ignored",
|
|
314
|
+
reason: anyKnowsKey
|
|
315
|
+
? "drop-target-not-eligible"
|
|
316
|
+
: "drop-target-input-unknown",
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (matches.length > 1) {
|
|
320
|
+
// Ambiguity: more than one descriptor accepts both this card and this
|
|
321
|
+
// destination. Signal so callers/tests catch the descriptor design
|
|
322
|
+
// bug rather than the runtime silently picking one.
|
|
323
|
+
return { status: "ignored", reason: "ambiguous-drop" };
|
|
324
|
+
}
|
|
325
|
+
descriptor = matches[0]!.descriptor;
|
|
326
|
+
cardInputKey = matches[0]!.cardInputKey;
|
|
327
|
+
dropTarget = matches[0]!.dropTarget;
|
|
328
|
+
} else {
|
|
329
|
+
descriptor = cardEligible[0]!.descriptor;
|
|
330
|
+
cardInputKey = cardEligible[0]!.cardInputKey;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!isInteractionAvailable(descriptor)) {
|
|
334
|
+
return { status: "ignored", reason: "interaction-unavailable" };
|
|
335
|
+
}
|
|
336
|
+
if (!inputByKey(descriptor, cardInputKey)) {
|
|
337
|
+
return { status: "ignored", reason: "no-card-input" };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const routed: RoutedInteractionTargetResult = routeCardInputIntent(
|
|
341
|
+
ctx.store,
|
|
342
|
+
descriptor,
|
|
343
|
+
{
|
|
344
|
+
cardInputKey,
|
|
345
|
+
cardId: intent.cardId,
|
|
346
|
+
dropTarget,
|
|
347
|
+
},
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
if (shouldRouteInteractionPending(descriptor, routed.readiness)) {
|
|
351
|
+
markInteractionPending(ctx.store, descriptor);
|
|
352
|
+
return {
|
|
353
|
+
status: "pending",
|
|
354
|
+
descriptor,
|
|
355
|
+
readiness: routed.readiness,
|
|
356
|
+
params: routed.params,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!claimInteractionSubmit(ctx.store, descriptor)) {
|
|
361
|
+
return {
|
|
362
|
+
status: "submitting",
|
|
363
|
+
descriptor,
|
|
364
|
+
readiness: routed.readiness,
|
|
365
|
+
params: routed.params,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
await ctx.submit(descriptor, routed.params);
|
|
371
|
+
clearInteractionRoute(ctx.store, descriptor);
|
|
372
|
+
return {
|
|
373
|
+
status: "submitted",
|
|
374
|
+
descriptor,
|
|
375
|
+
readiness: routed.readiness,
|
|
376
|
+
params: routed.params,
|
|
377
|
+
};
|
|
378
|
+
} catch (error) {
|
|
379
|
+
return { status: "error", descriptor, error };
|
|
380
|
+
} finally {
|
|
381
|
+
ctx.store.setSubmitting(descriptor.interactionKey, false);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Resolve a typed drop-target kind/value pair to the descriptor's collector
|
|
387
|
+
* input key. Authors describe targets at the type level (e.g. board space
|
|
388
|
+
* `hex-a`) but the descriptor names that input by its authored collector
|
|
389
|
+
* key (e.g. `spaceId`). This bridges the two without surfacing collector
|
|
390
|
+
* names in the authoring API.
|
|
391
|
+
*/
|
|
392
|
+
function resolveDropTargetInputKey(
|
|
393
|
+
descriptor: InteractionDescriptor,
|
|
394
|
+
kind: RuntimeDropTargetKind,
|
|
395
|
+
value: string,
|
|
396
|
+
): string | null {
|
|
397
|
+
// Prefer an input whose authoritative resolved domain advertises this
|
|
398
|
+
// value as eligible. Falls back to a kind-only match for lazy domains.
|
|
399
|
+
let kindOnlyMatch: string | null = null;
|
|
400
|
+
for (const input of descriptor.inputs) {
|
|
401
|
+
if (!matchesDropTargetKind(input.domain, kind)) continue;
|
|
402
|
+
if (
|
|
403
|
+
isResolvedTargetDomain(input.domain) &&
|
|
404
|
+
input.domain.eligibleTargets.includes(value)
|
|
405
|
+
) {
|
|
406
|
+
return input.key;
|
|
407
|
+
}
|
|
408
|
+
if (kindOnlyMatch === null) {
|
|
409
|
+
kindOnlyMatch = input.key;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return kindOnlyMatch;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function matchesDropTargetKind(
|
|
416
|
+
domain: InteractionDescriptor["inputs"][number]["domain"],
|
|
417
|
+
kind: RuntimeDropTargetKind,
|
|
418
|
+
): boolean {
|
|
419
|
+
if (kind === "card") {
|
|
420
|
+
return isTargetDomain(domain) && domain.type === "cardTarget";
|
|
421
|
+
}
|
|
422
|
+
return (
|
|
423
|
+
isTargetDomain(domain) &&
|
|
424
|
+
domain.type === "boardTarget" &&
|
|
425
|
+
domain.targetKind === kind
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Project descriptor + draft state into per-card visual flags used by the
|
|
431
|
+
* runtime hand surface. Each entry tracks which card ids are currently part
|
|
432
|
+
* of any card-target draft, and which of those have field validation errors.
|
|
433
|
+
*/
|
|
434
|
+
export interface DraftCardProjectionEntry {
|
|
435
|
+
descriptor: InteractionDescriptor;
|
|
436
|
+
cardInputKeys: readonly string[];
|
|
437
|
+
draftCardIds: ReadonlySet<string>;
|
|
438
|
+
invalidCardIds: ReadonlySet<string>;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export function projectDraftCardState(
|
|
442
|
+
availableInteractions: readonly InteractionDescriptor[],
|
|
443
|
+
drafts: Readonly<Record<string, Readonly<Record<string, unknown>>>>,
|
|
444
|
+
validate: (
|
|
445
|
+
descriptor: InteractionDescriptor,
|
|
446
|
+
draft: Readonly<Record<string, unknown>>,
|
|
447
|
+
) => Partial<Record<string, readonly string[]>>,
|
|
448
|
+
): DraftCardProjectionEntry[] {
|
|
449
|
+
const entries: DraftCardProjectionEntry[] = [];
|
|
450
|
+
for (const descriptor of availableInteractions) {
|
|
451
|
+
const cardInputs = descriptor.inputs.filter(
|
|
452
|
+
(input) =>
|
|
453
|
+
isTargetDomain(input.domain) && input.domain.type === "cardTarget",
|
|
454
|
+
);
|
|
455
|
+
if (cardInputs.length === 0) continue;
|
|
456
|
+
const draft = drafts[descriptor.interactionKey] ?? {};
|
|
457
|
+
const draftCardIds = new Set<string>();
|
|
458
|
+
for (const input of cardInputs) {
|
|
459
|
+
const value = draft[input.key];
|
|
460
|
+
if (Array.isArray(value)) {
|
|
461
|
+
for (const item of value) {
|
|
462
|
+
if (typeof item === "string") draftCardIds.add(item);
|
|
463
|
+
}
|
|
464
|
+
} else if (typeof value === "string") {
|
|
465
|
+
draftCardIds.add(value);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const fieldErrors = validate(descriptor, draft);
|
|
469
|
+
const invalidCardIds = new Set<string>();
|
|
470
|
+
for (const input of cardInputs) {
|
|
471
|
+
if ((fieldErrors[input.key]?.length ?? 0) === 0) continue;
|
|
472
|
+
const value = draft[input.key];
|
|
473
|
+
if (Array.isArray(value)) {
|
|
474
|
+
for (const item of value) {
|
|
475
|
+
if (typeof item === "string") invalidCardIds.add(item);
|
|
476
|
+
}
|
|
477
|
+
} else if (typeof value === "string") {
|
|
478
|
+
invalidCardIds.add(value);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
entries.push({
|
|
482
|
+
descriptor,
|
|
483
|
+
cardInputKeys: cardInputs.map((input) => input.key),
|
|
484
|
+
draftCardIds,
|
|
485
|
+
invalidCardIds,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
return entries;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
export interface ProjectedCardVisualState {
|
|
492
|
+
selected: boolean;
|
|
493
|
+
invalid: boolean;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Combine the per-descriptor projection into a single per-card flag tuple.
|
|
498
|
+
*/
|
|
499
|
+
export function visualStateForCard(
|
|
500
|
+
cardId: string,
|
|
501
|
+
projection: readonly DraftCardProjectionEntry[],
|
|
502
|
+
): ProjectedCardVisualState {
|
|
503
|
+
let selected = false;
|
|
504
|
+
let invalid = false;
|
|
505
|
+
for (const entry of projection) {
|
|
506
|
+
if (entry.draftCardIds.has(cardId)) selected = true;
|
|
507
|
+
if (entry.invalidCardIds.has(cardId)) invalid = true;
|
|
508
|
+
}
|
|
509
|
+
return { selected, invalid };
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Return the set of card ids that are currently part of a many-selection
|
|
514
|
+
* draft for any descriptor in the zone. Used by the hand view to drop
|
|
515
|
+
* selected cards out of the visible hand layout while keeping the runtime
|
|
516
|
+
* source of truth in the zone snapshot.
|
|
517
|
+
*/
|
|
518
|
+
export function selectedCardIdsForZone(
|
|
519
|
+
store: Pick<InteractionUiStore, "getDraft">,
|
|
520
|
+
zone: string,
|
|
521
|
+
state: PluginStateSnapshot,
|
|
522
|
+
): readonly string[] {
|
|
523
|
+
const result = new Set<string>();
|
|
524
|
+
for (const descriptor of state.gameplay.availableInteractions) {
|
|
525
|
+
const cardInput = descriptor.inputs.find((input) => {
|
|
526
|
+
if (!isTargetDomain(input.domain)) return false;
|
|
527
|
+
if (input.domain.type !== "cardTarget") return false;
|
|
528
|
+
const declaredZones =
|
|
529
|
+
input.domain.zoneId !== undefined
|
|
530
|
+
? [input.domain.zoneId]
|
|
531
|
+
: (input.domain.zoneIds ?? []);
|
|
532
|
+
return declaredZones.length === 0 || declaredZones.includes(zone);
|
|
533
|
+
});
|
|
534
|
+
if (!cardInput) continue;
|
|
535
|
+
if (!isTargetDomain(cardInput.domain)) continue;
|
|
536
|
+
if (cardInput.domain.type !== "cardTarget") continue;
|
|
537
|
+
const draft = store.getDraft(descriptor.interactionKey);
|
|
538
|
+
const value = draft[cardInput.key];
|
|
539
|
+
if (cardInput.domain.selection?.mode === "many" && Array.isArray(value)) {
|
|
540
|
+
for (const item of value) {
|
|
541
|
+
if (typeof item === "string") result.add(item);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return [...result];
|
|
546
|
+
}
|