@dreamboard-games/sdk 0.2.0 → 0.2.1-alpha.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 (90) hide show
  1. package/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-BBMVT3KG.d.ts} +1 -1
  2. package/dist/attributes-BeRyboMS.d.ts +279 -0
  3. package/dist/browser-interaction.d.ts +708 -0
  4. package/dist/browser-interaction.js +106 -0
  5. package/dist/browser-interaction.js.map +1 -0
  6. package/dist/{bundle-TIZcw8LB.d.ts → bundle-CDd5FKeD.d.ts} +3 -1
  7. package/dist/{chunk-U5C6BONG.js → chunk-326PGVAA.js} +2 -2
  8. package/dist/{chunk-VFTAA4WO.js → chunk-MKXPVOUT.js} +4 -2
  9. package/dist/chunk-MKXPVOUT.js.map +1 -0
  10. package/dist/{chunk-GKKBPPSW.js → chunk-MZNVHMJ5.js} +4 -4
  11. package/dist/{chunk-KAELH4KC.js → chunk-NKCRKGR2.js} +2 -2
  12. package/dist/{chunk-WN74KVNY.js → chunk-PEI3FIL2.js} +2 -2
  13. package/dist/chunk-PEI3FIL2.js.map +1 -0
  14. package/dist/chunk-QLG6VEMW.js +1691 -0
  15. package/dist/chunk-QLG6VEMW.js.map +1 -0
  16. package/dist/{chunk-WYPQ3GG5.js → chunk-WG4JQL3S.js} +4 -1
  17. package/dist/{chunk-WYPQ3GG5.js.map → chunk-WG4JQL3S.js.map} +1 -1
  18. package/dist/{chunk-7YAHLYBR.js → chunk-XV6D3ET4.js} +8 -4
  19. package/dist/{chunk-7YAHLYBR.js.map → chunk-XV6D3ET4.js.map} +1 -1
  20. package/dist/{chunk-TDSWKVZ4.js → chunk-ZABVH7AO.js} +1236 -17
  21. package/dist/chunk-ZABVH7AO.js.map +1 -0
  22. package/dist/{components-D5ZRE2Hl.d.ts → components-BoiVSYqx.d.ts} +1 -1
  23. package/dist/generated/runtime/primitives.d.ts +5 -4
  24. package/dist/generated/runtime/primitives.js +4 -3
  25. package/dist/generated/runtime-api.d.ts +1 -1
  26. package/dist/generated/runtime.d.ts +5 -4
  27. package/dist/generated/runtime.js +7 -6
  28. package/dist/generated/workspace-contract.d.ts +5 -4
  29. package/dist/generated/workspace-contract.js +6 -5
  30. package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-1iAyJRFn.d.ts} +1 -0
  31. package/dist/index.js +1 -1
  32. package/dist/infrastructure/reducer-bundle-abi.d.ts +113 -113
  33. package/dist/infrastructure/reducer-bundle-abi.js +1 -1
  34. package/dist/package-set.d.ts +2 -2
  35. package/dist/package-set.js +1 -1
  36. package/dist/reducer.d.ts +1 -1
  37. package/dist/reducer.js +305 -12
  38. package/dist/reducer.js.map +1 -1
  39. package/dist/runtime/primitives.d.ts +6 -5
  40. package/dist/runtime/primitives.js +4 -3
  41. package/dist/runtime/workspace-contract.d.ts +6 -5
  42. package/dist/runtime/workspace-contract.js +6 -5
  43. package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-CPLm_XDG.d.ts} +6 -0
  44. package/dist/runtime.d.ts +5 -4
  45. package/dist/runtime.js +6 -5
  46. package/dist/testing.d.ts +2 -2
  47. package/dist/ui/components.d.ts +2 -2
  48. package/dist/ui/components.js +1 -1
  49. package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-rzKBwOLC.d.ts} +5 -3
  50. package/dist/ui.d.ts +5 -5
  51. package/dist/ui.js +2 -2
  52. package/package.json +15 -9
  53. package/src/browser-interaction/attributes.ts +211 -0
  54. package/src/browser-interaction/canonical.ts +77 -0
  55. package/src/browser-interaction/constants.ts +77 -0
  56. package/src/browser-interaction/effects.ts +176 -0
  57. package/src/browser-interaction/index.ts +111 -0
  58. package/src/browser-interaction/normalize.ts +997 -0
  59. package/src/browser-interaction/registry.ts +70 -0
  60. package/src/browser-interaction/resolve.ts +596 -0
  61. package/src/browser-interaction/schemas.ts +152 -0
  62. package/src/browser-interaction/types.ts +304 -0
  63. package/src/browser-interaction.ts +1 -0
  64. package/src/generated/reducer-contract/wire.ts +1 -1
  65. package/src/generated/reducer-contract/zod.ts +3 -1
  66. package/src/package-set.ts +1 -1
  67. package/src/reducer/bundle/ingress-bundle.ts +1 -1
  68. package/src/reducer/bundle/trusted/interaction-types.ts +3 -0
  69. package/src/reducer/bundle/trusted/projection-builder.ts +337 -13
  70. package/src/reducer/ingress/input-codec.ts +1 -1
  71. package/src/reducer/ingress/session-codec.ts +1 -1
  72. package/src/runtime-internal/components/InteractionForm.tsx +345 -7
  73. package/src/runtime-internal/components/PluginRuntime.tsx +2 -0
  74. package/src/runtime-internal/components/board/target-layer.ts +2 -0
  75. package/src/runtime-internal/context/PluginStateContext.tsx +41 -0
  76. package/src/runtime-internal/hooks/useBoardInteractions.ts +73 -11
  77. package/src/runtime-internal/primitives/board.tsx +71 -0
  78. package/src/runtime-internal/primitives/interaction.tsx +160 -1
  79. package/src/runtime-internal/types/plugin-state.ts +6 -0
  80. package/src/runtime-internal/utils/browser-interaction-effects.ts +240 -0
  81. package/src/runtime-internal/utils/interaction-draft-digest.ts +252 -0
  82. package/src/runtime-internal/utils/semantic-projection-digest.ts +407 -0
  83. package/src/ui/components/board/HexGrid.tsx +3 -0
  84. package/src/ui/components/board/target-layer.ts +1 -0
  85. package/dist/chunk-TDSWKVZ4.js.map +0 -1
  86. package/dist/chunk-VFTAA4WO.js.map +0 -1
  87. package/dist/chunk-WN74KVNY.js.map +0 -1
  88. /package/dist/{chunk-U5C6BONG.js.map → chunk-326PGVAA.js.map} +0 -0
  89. /package/dist/{chunk-GKKBPPSW.js.map → chunk-MZNVHMJ5.js.map} +0 -0
  90. /package/dist/{chunk-KAELH4KC.js.map → chunk-NKCRKGR2.js.map} +0 -0
@@ -30,6 +30,12 @@ import {
30
30
  shouldRouteInteractionPending,
31
31
  } from "../utils/interaction-router.js";
32
32
  import { isInteractionAvailable } from "../utils/interaction-status.js";
33
+ import {
34
+ createGameplayActuatorAttributes,
35
+ type BrowserInteractionAttributeMap,
36
+ } from "../../browser-interaction/index.js";
37
+ import { interactionDraftDigestForValues } from "../utils/interaction-draft-digest.js";
38
+ import { gameplayCandidateMetadata } from "../utils/browser-interaction-effects.js";
33
39
 
34
40
  export type BoardEligibleTargets = Readonly<
35
41
  Record<BoardTargetKind, ReadonlySet<string>>
@@ -379,7 +385,7 @@ export function useBoardInteractions<I extends string = string>(
379
385
  return [];
380
386
  }
381
387
  if (!isInteractionAvailable(descriptor)) return [];
382
- const draft = store.getDraft(descriptor.interactionKey);
388
+ const draft = drafts[descriptor.interactionKey] ?? {};
383
389
  const inputKey = inputKeyForTarget(
384
390
  descriptor,
385
391
  targetKind,
@@ -388,14 +394,7 @@ export function useBoardInteractions<I extends string = string>(
388
394
  );
389
395
  if (!inputKey) return [];
390
396
  const input = inputByTarget(descriptor, targetKind, targetId, draft);
391
- if (
392
- input &&
393
- !isTargetSelectable(
394
- input,
395
- store.getDraft(descriptor.interactionKey),
396
- targetId,
397
- )
398
- ) {
397
+ if (input && !isTargetSelectable(input, draft, targetId)) {
399
398
  return [];
400
399
  }
401
400
  const targets = eligibleTargetsForInput(descriptor, inputKey, draft);
@@ -410,7 +409,7 @@ export function useBoardInteractions<I extends string = string>(
410
409
  });
411
410
  return matches;
412
411
  },
413
- [armedIds, interactions, store],
412
+ [armedIds, drafts, interactions],
414
413
  );
415
414
 
416
415
  const selectByKind = useCallback(
@@ -486,9 +485,19 @@ export function useBoardInteractions<I extends string = string>(
486
485
  ? candidates.map((candidate) => candidate.descriptor.interactionKey)
487
486
  : undefined,
488
487
  unavailableReason: candidateUnavailableReason(selected, conflict),
488
+ browserAttributes: selected
489
+ ? boardTargetBrowserAttributes({
490
+ descriptor: selected.descriptor,
491
+ inputKey: selected.inputKey,
492
+ targetKind,
493
+ targetId,
494
+ enabled: eligible && !!controllingPlayerId,
495
+ draft: drafts[selected.descriptor.interactionKey] ?? {},
496
+ })
497
+ : undefined,
489
498
  };
490
499
  },
491
- [controllingPlayerId, pendingInteractionKey, resolveTargetMatches],
500
+ [controllingPlayerId, drafts, pendingInteractionKey, resolveTargetMatches],
492
501
  );
493
502
 
494
503
  const targetLayers = useMemo(() => {
@@ -634,6 +643,59 @@ interface MatchingDescriptor<I extends string> {
634
643
  armed: boolean;
635
644
  }
636
645
 
646
+ const GAMEPLAY_BROWSER_SCOPE_ID = "runtime";
647
+
648
+ function boardTargetBrowserAttributes({
649
+ descriptor,
650
+ inputKey,
651
+ targetKind,
652
+ targetId,
653
+ enabled,
654
+ draft,
655
+ }: {
656
+ descriptor: InteractionDescriptor;
657
+ inputKey: string;
658
+ targetKind: BoardTargetKind;
659
+ targetId: string;
660
+ enabled: boolean;
661
+ draft: Readonly<Record<string, unknown>>;
662
+ }): BrowserInteractionAttributeMap {
663
+ return createGameplayActuatorAttributes({
664
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID,
665
+ interactionKey: descriptor.interactionKey,
666
+ interactionId: descriptor.interactionId,
667
+ intent: "select",
668
+ enabled,
669
+ actuatorKind: "click",
670
+ actuatorId: `board:${targetKind}:${targetId}`,
671
+ ...(descriptor.descriptorDigest !== undefined
672
+ ? { descriptorDigest: descriptor.descriptorDigest }
673
+ : {}),
674
+ ...(descriptor.draftDigest !== undefined
675
+ ? { draftDigest: interactionDraftDigestForValues(descriptor, draft) }
676
+ : {}),
677
+ inputKey,
678
+ candidateValue: targetId,
679
+ candidateState: isBoardTargetSelected(draft[inputKey], targetId)
680
+ ? "selected"
681
+ : "unselected",
682
+ semanticEffects: gameplayCandidateMetadata({
683
+ descriptor,
684
+ draftValues: draft,
685
+ inputKey,
686
+ candidateValue: targetId,
687
+ intent: "select",
688
+ }).semanticEffects,
689
+ });
690
+ }
691
+
692
+ function isBoardTargetSelected(value: unknown, targetId: string): boolean {
693
+ if (Array.isArray(value)) {
694
+ return value.some((item) => String(item) === targetId);
695
+ }
696
+ return value !== undefined && String(value) === targetId;
697
+ }
698
+
637
699
  function selectDispatchCandidate<I extends string>(
638
700
  matches: ReadonlyArray<MatchingDescriptor<I>>,
639
701
  targetKind: BoardTargetKind,
@@ -40,6 +40,12 @@ import { runInteractionAction } from "./interaction-submit.js";
40
40
  import { useGameActionError } from "./game.js";
41
41
  import { isInteractionAvailable } from "../utils/interaction-status.js";
42
42
  import { useInteractionUiStore } from "../context/InteractionDraftContext.js";
43
+ import {
44
+ createGameplayActuatorAttributes,
45
+ type BrowserInteractionAttributeMap,
46
+ } from "../../browser-interaction/index.js";
47
+ import { interactionDraftDigestForValues } from "../utils/interaction-draft-digest.js";
48
+ import { gameplayCandidateMetadata } from "../utils/browser-interaction-effects.js";
43
49
 
44
50
  type BoardContextValue = BoardInteractionsContext;
45
51
 
@@ -312,6 +318,7 @@ function UnambiguousBoardTarget({
312
318
  return renderPrimitive("button", {
313
319
  type: "button",
314
320
  ...props,
321
+ ...targetState.browserAttributes,
315
322
  children,
316
323
  disabled: isDisabled,
317
324
  "aria-disabled": isDisabled,
@@ -377,9 +384,20 @@ function ExplicitBoardTarget({
377
384
  (eligibleTargets ?? inputDescriptor.domain.eligibleTargets).includes(value);
378
385
  const isDisabled =
379
386
  disabled ?? (!isInteractionAvailable(descriptor) || !eligible);
387
+ const browserAttributes = inputKey
388
+ ? boardTargetBrowserAttributes({
389
+ descriptor,
390
+ inputKey,
391
+ targetKind: kind,
392
+ targetId: value,
393
+ enabled: !isDisabled,
394
+ draft,
395
+ })
396
+ : undefined;
380
397
  return renderPrimitive("button", {
381
398
  type: "button",
382
399
  ...props,
400
+ ...browserAttributes,
383
401
  children,
384
402
  disabled: isDisabled,
385
403
  "aria-disabled": isDisabled,
@@ -447,6 +465,59 @@ function resolveExtraInputs(
447
465
  : (extraInputs ?? {});
448
466
  }
449
467
 
468
+ const GAMEPLAY_BROWSER_SCOPE_ID = "runtime";
469
+
470
+ function boardTargetBrowserAttributes({
471
+ descriptor,
472
+ inputKey,
473
+ targetKind,
474
+ targetId,
475
+ enabled,
476
+ draft,
477
+ }: {
478
+ descriptor: InteractionDescriptor;
479
+ inputKey: string;
480
+ targetKind: BoardTargetKind;
481
+ targetId: string;
482
+ enabled: boolean;
483
+ draft: Readonly<Record<string, unknown>>;
484
+ }): BrowserInteractionAttributeMap {
485
+ return createGameplayActuatorAttributes({
486
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID,
487
+ interactionKey: descriptor.interactionKey,
488
+ interactionId: descriptor.interactionId,
489
+ intent: "select",
490
+ enabled,
491
+ actuatorKind: "click",
492
+ actuatorId: `board:${targetKind}:${targetId}`,
493
+ ...(descriptor.descriptorDigest !== undefined
494
+ ? { descriptorDigest: descriptor.descriptorDigest }
495
+ : {}),
496
+ ...(descriptor.draftDigest !== undefined
497
+ ? { draftDigest: interactionDraftDigestForValues(descriptor, draft) }
498
+ : {}),
499
+ inputKey,
500
+ candidateValue: targetId,
501
+ candidateState: isBoardTargetSelected(draft[inputKey], targetId)
502
+ ? "selected"
503
+ : "unselected",
504
+ semanticEffects: gameplayCandidateMetadata({
505
+ descriptor,
506
+ draftValues: draft,
507
+ inputKey,
508
+ candidateValue: targetId,
509
+ intent: "select",
510
+ }).semanticEffects,
511
+ });
512
+ }
513
+
514
+ function isBoardTargetSelected(value: unknown, targetId: string): boolean {
515
+ if (Array.isArray(value)) {
516
+ return value.some((item) => String(item) === targetId);
517
+ }
518
+ return value !== undefined && String(value) === targetId;
519
+ }
520
+
450
521
  export const Board = {
451
522
  Root: BoardRoot,
452
523
  State: BoardState,
@@ -34,6 +34,7 @@ import {
34
34
  isResolvedTargetDomain,
35
35
  isTargetDomain,
36
36
  isManyTargetSelectable,
37
+ resolveInputDomain,
37
38
  } from "../utils/interaction-inputs.js";
38
39
  import {
39
40
  getInteractionDraftReadiness,
@@ -45,12 +46,25 @@ import {
45
46
  interactionUnavailableReason,
46
47
  isInteractionAvailable,
47
48
  } from "../utils/interaction-status.js";
49
+ import {
50
+ gameplayCandidateMetadata,
51
+ gameplayPreparationPatternsForDescriptor,
52
+ gameplayScalarFillMetadata,
53
+ gameplaySubmitMetadata,
54
+ } from "../utils/browser-interaction-effects.js";
48
55
  import { interactionLabel } from "../utils/interaction-labels.js";
49
56
  import {
50
57
  composeEventHandlers,
51
58
  renderPrimitive,
52
59
  type PrimitiveCommonProps,
53
60
  } from "./primitive-props.js";
61
+ import {
62
+ createGameplayActuatorAttributes,
63
+ createGameplayInteractionRootAttributes,
64
+ type BrowserInteractionAttributeMap,
65
+ type GameplayActuatorAttributesInput,
66
+ type GameplayBrowserInteractionIntent,
67
+ } from "../../browser-interaction/index.js";
54
68
  import {
55
69
  BoundInteractionForm,
56
70
  castInteractionDraft,
@@ -92,6 +106,56 @@ function humanizeInteraction(value: string): string {
92
106
  .replace(/^./, (first: string) => first.toUpperCase());
93
107
  }
94
108
 
109
+ const GAMEPLAY_BROWSER_SCOPE_ID = "runtime";
110
+
111
+ function gameplayActuatorAttributes({
112
+ descriptor,
113
+ inputKey,
114
+ intent,
115
+ candidateValue,
116
+ candidateState,
117
+ semanticEffects,
118
+ acceptedEffectPatterns,
119
+ preparationPatterns,
120
+ enabled,
121
+ actuatorKind,
122
+ actuatorId,
123
+ }: {
124
+ descriptor: InteractionDescriptor;
125
+ inputKey?: string;
126
+ intent: GameplayBrowserInteractionIntent;
127
+ candidateValue?: unknown;
128
+ candidateState?: "selected" | "unselected" | "mixed";
129
+ semanticEffects?: GameplayActuatorAttributesInput["semanticEffects"];
130
+ acceptedEffectPatterns?: GameplayActuatorAttributesInput["acceptedEffectPatterns"];
131
+ preparationPatterns?: GameplayActuatorAttributesInput["preparationPatterns"];
132
+ enabled: boolean;
133
+ actuatorKind: GameplayActuatorAttributesInput["actuatorKind"];
134
+ actuatorId: string;
135
+ }): BrowserInteractionAttributeMap {
136
+ return createGameplayActuatorAttributes({
137
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID,
138
+ interactionKey: descriptor.interactionKey,
139
+ interactionId: descriptor.interactionId,
140
+ intent,
141
+ enabled,
142
+ actuatorKind,
143
+ actuatorId,
144
+ ...(descriptor.descriptorDigest !== undefined
145
+ ? { descriptorDigest: descriptor.descriptorDigest }
146
+ : {}),
147
+ ...(descriptor.draftDigest !== undefined
148
+ ? { draftDigest: descriptor.draftDigest }
149
+ : {}),
150
+ ...(inputKey !== undefined ? { inputKey } : {}),
151
+ ...(candidateValue !== undefined ? { candidateValue } : {}),
152
+ ...(candidateState !== undefined ? { candidateState } : {}),
153
+ ...(semanticEffects !== undefined ? { semanticEffects } : {}),
154
+ ...(acceptedEffectPatterns !== undefined ? { acceptedEffectPatterns } : {}),
155
+ ...(preparationPatterns !== undefined ? { preparationPatterns } : {}),
156
+ });
157
+ }
158
+
95
159
  export function useInteractionPrimitiveContext(): InteractionContextValue {
96
160
  const value = useContext(InteractionContext);
97
161
  if (!value) {
@@ -155,9 +219,30 @@ function ResolvedInteractionRoot({
155
219
  () => ({ interaction, descriptor, handle }),
156
220
  [descriptor, handle, interaction],
157
221
  );
222
+ const available = isInteractionAvailable(descriptor);
223
+ const rootAttributes = createGameplayInteractionRootAttributes({
224
+ scopeId: GAMEPLAY_BROWSER_SCOPE_ID,
225
+ interactionKey: descriptor.interactionKey,
226
+ interactionId: descriptor.interactionId,
227
+ ...(descriptor.descriptorDigest !== undefined
228
+ ? { descriptorDigest: descriptor.descriptorDigest }
229
+ : {}),
230
+ ...(descriptor.draftDigest !== undefined
231
+ ? { draftDigest: descriptor.draftDigest }
232
+ : {}),
233
+ readiness: available
234
+ ? handle.isReady
235
+ ? "ready"
236
+ : "blocked"
237
+ : "unavailable",
238
+ });
158
239
  return (
159
240
  <InteractionContext.Provider value={value}>
160
- {children}
241
+ {renderPrimitive("span", {
242
+ ...rootAttributes,
243
+ style: { display: "contents" },
244
+ children,
245
+ })}
161
246
  </InteractionContext.Provider>
162
247
  );
163
248
  }
@@ -447,6 +532,19 @@ export function InteractionTrigger({
447
532
  return renderPrimitive("button", {
448
533
  type: "button",
449
534
  ...props,
535
+ ...(descriptor
536
+ ? gameplayActuatorAttributes({
537
+ descriptor,
538
+ intent: "arm",
539
+ enabled: !isDisabled,
540
+ actuatorKind: "click",
541
+ actuatorId: "primitive-trigger",
542
+ preparationPatterns: gameplayPreparationPatternsForDescriptor(
543
+ descriptor,
544
+ (handle?.values ?? {}) as Readonly<Record<string, unknown>>,
545
+ ),
546
+ })
547
+ : {}),
450
548
  disabled: isDisabled,
451
549
  "aria-disabled": isDisabled,
452
550
  "data-dreamboard-interaction-trigger": "",
@@ -595,6 +693,9 @@ export function InteractionSubmit({
595
693
  const gameActionError = useGameActionError();
596
694
  const isSubmitting = handle?.status === "submitting";
597
695
  const hasExplicitParams = params !== undefined;
696
+ const submitMetadata = descriptor
697
+ ? gameplaySubmitMetadata({ descriptor, explicitParams: hasExplicitParams })
698
+ : null;
598
699
  const available = isInteractionAvailable(descriptor);
599
700
  const isDisabled =
600
701
  disabled === true ||
@@ -604,6 +705,16 @@ export function InteractionSubmit({
604
705
  return renderPrimitive("button", {
605
706
  type: "button",
606
707
  ...props,
708
+ ...(descriptor
709
+ ? gameplayActuatorAttributes({
710
+ descriptor,
711
+ intent: submitMetadata?.intent ?? "submit",
712
+ enabled: !isDisabled,
713
+ actuatorKind: "click",
714
+ actuatorId: "primitive-submit",
715
+ semanticEffects: submitMetadata?.semanticEffects,
716
+ })
717
+ : {}),
607
718
  disabled: isDisabled,
608
719
  "aria-disabled": isDisabled,
609
720
  "data-dreamboard-interaction-submit": "",
@@ -650,9 +761,35 @@ export function InteractionInput({
650
761
  const { descriptor, handle } = useInteractionPrimitiveContext();
651
762
  const value = handle?.draft[name];
652
763
  const isDisabled = disabled === true || !isInteractionAvailable(descriptor);
764
+ const inputDescriptor = descriptor ? inputByKey(descriptor, name) : undefined;
765
+ const resolvedInputDescriptor =
766
+ inputDescriptor && handle
767
+ ? resolveInputDomain(
768
+ inputDescriptor,
769
+ handle.values as Readonly<Record<string, unknown>>,
770
+ )
771
+ : undefined;
772
+ const scalarFillMetadata =
773
+ resolvedInputDescriptor?.domain.type === "boundedNumber"
774
+ ? gameplayScalarFillMetadata({
775
+ inputKey: name,
776
+ domain: resolvedInputDescriptor.domain,
777
+ })
778
+ : undefined;
653
779
  return renderPrimitive("input", {
654
780
  ...props,
655
781
  name,
782
+ ...(descriptor
783
+ ? gameplayActuatorAttributes({
784
+ descriptor,
785
+ inputKey: name,
786
+ intent: "fill",
787
+ enabled: !isDisabled,
788
+ actuatorKind: "fill",
789
+ actuatorId: `primitive-input:${name}`,
790
+ acceptedEffectPatterns: scalarFillMetadata?.acceptedEffectPatterns,
791
+ })
792
+ : {}),
656
793
  disabled: isDisabled,
657
794
  "aria-disabled": isDisabled,
658
795
  "data-dreamboard-interaction-input": "",
@@ -850,6 +987,28 @@ export function InteractionCardInput<
850
987
  type: "button",
851
988
  ...props,
852
989
  children: renderedChildren,
990
+ ...(descriptor
991
+ ? gameplayActuatorAttributes({
992
+ descriptor,
993
+ inputKey: input,
994
+ intent: selection?.mode === "many" ? "toggle" : "select",
995
+ candidateValue: cardId,
996
+ candidateState: isSelected ? "selected" : "unselected",
997
+ enabled: !isDisabled,
998
+ actuatorKind: "click",
999
+ actuatorId: `primitive-card:${input}:${cardId ?? "missing"}`,
1000
+ semanticEffects:
1001
+ cardId !== undefined
1002
+ ? gameplayCandidateMetadata({
1003
+ descriptor,
1004
+ draftValues: liveDraft,
1005
+ inputKey: input,
1006
+ candidateValue: cardId,
1007
+ intent: selection?.mode === "many" ? "toggle" : "select",
1008
+ }).semanticEffects
1009
+ : undefined,
1010
+ })
1011
+ : {}),
853
1012
  disabled: isDisabled,
854
1013
  "aria-disabled": isDisabled,
855
1014
  "aria-pressed": isSelected,
@@ -388,6 +388,12 @@ interface InteractionDescriptorBase<Key extends string = string> {
388
388
  interactionId: string;
389
389
  /** Draft commit policy. Always materialized by the trusted reducer bundle. */
390
390
  commit: InteractionCommitPolicy;
391
+ /** Canonical descriptor digest used by browser replay/protocol tooling when projected by the host. */
392
+ descriptorDigest?: string;
393
+ /** Explicit authoring/runtime actor seat used by browser replay draft digests. */
394
+ actorSeat?: number;
395
+ /** Canonical draft digest used by browser replay/protocol tooling when projected by the host. */
396
+ draftDigest?: string;
391
397
  /** Source zone id for zone-scoped interactions (e.g., cardInput). */
392
398
  zoneId?: string;
393
399
  /** Source zone ids for zone-scoped interactions that span multiple zones. */